Finish NCAContent refactoring
continuous-integration/drone/push Build is passing Details

master
Dmitry Isaenko 2022-09-12 01:02:22 +03:00
parent 0238cb4d2c
commit c878992cb6
1 changed files with 45 additions and 111 deletions

View File

@ -18,23 +18,22 @@
*/ */
package libKonogonka.Tools.NCA; package libKonogonka.Tools.NCA;
import libKonogonka.Converter;
import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader; import libKonogonka.Tools.NCA.NCASectionTableBlock.NcaFsHeader;
import libKonogonka.Tools.PFS0.IPFS0Provider; import libKonogonka.Tools.PFS0.IPFS0Provider;
import libKonogonka.Tools.PFS0.PFS0Provider; import libKonogonka.Tools.PFS0.PFS0Provider;
import libKonogonka.Tools.RomFs.IRomFsProvider; import libKonogonka.Tools.RomFs.IRomFsProvider;
import libKonogonka.Tools.RomFs.RomFsEncryptedProvider; import libKonogonka.Tools.RomFs.RomFsEncryptedProvider;
import libKonogonka.ctraes.AesCtrBufferedInputStream;
import libKonogonka.ctraes.AesCtrDecryptSimple; import libKonogonka.ctraes.AesCtrDecryptSimple;
import libKonogonka.exceptions.EmptySectionException; import libKonogonka.exceptions.EmptySectionException;
import org.apache.logging.log4j.LogManager; import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.Logger;
import java.io.*; import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.LinkedList; import java.util.LinkedList;
/**
* THIS CLASS BECOMES MORE UGLY AFTER EACH ITERATION OF REFACTORING.
* TODO: MAKE SOME DECOMPOSITION
* */
public class NCAContent { public class NCAContent {
private final static Logger log = LogManager.getLogger(NCAContent.class); private final static Logger log = LogManager.getLogger(NCAContent.class);
@ -48,7 +47,7 @@ public class NCAContent {
private IPFS0Provider pfs0; private IPFS0Provider pfs0;
private IRomFsProvider romfs; private IRomFsProvider romfs;
// TODO: if decryptedKey is empty, throw exception ?? // TODO: if decryptedKey is empty, throw exception?
public NCAContent(File file, public NCAContent(File file,
long ncaOffsetPosition, long ncaOffsetPosition,
NcaFsHeader ncaFsHeader, NcaFsHeader ncaFsHeader,
@ -76,14 +75,14 @@ public class NCAContent {
private void proceedPFS0() throws Exception { private void proceedPFS0() throws Exception {
switch (ncaFsHeader.getCryptoType()){ switch (ncaFsHeader.getCryptoType()){
case 0x01: case 0x01: // IF NO ENCRYPTION
proceedPFS0NotEncrypted(); // IF NO ENCRYPTION proceedPFS0NotEncrypted();
break; break;
case 0x03: case 0x03:
proceedPFS0Encrypted(); // If encrypted regular [ 0x03 ] proceedPFS0Encrypted();
break; break;
default: default:
throw new Exception("NCAContent() -> proceedPFS0(): Non-supported 'Crypto type'"); throw new Exception("'Crypto type' not supported: "+ncaFsHeader.getCryptoType());
} }
} }
private void proceedPFS0NotEncrypted() throws Exception{ private void proceedPFS0NotEncrypted() throws Exception{
@ -135,120 +134,55 @@ public class NCAContent {
ncaHeaderTableEntry.getMediaStartOffset(), ncaHeaderTableEntry.getMediaStartOffset(),
ncaHeaderTableEntry.getMediaEndOffset()); ncaHeaderTableEntry.getMediaEndOffset());
} }
public LinkedList<byte[]> getPfs0SHA256hashes() { return Pfs0SHA256hashes; }
public IPFS0Provider getPfs0() { return pfs0; } public IPFS0Provider getPfs0() { return pfs0; }
public IRomFsProvider getRomfs() { return romfs; } public IRomFsProvider getRomfs() { return romfs; }
/** /**
* Export NCA content AS IS. * Export NCA content AS IS.
* Not so good for PFS0 since there are SHAs list that discourages but good for 'romfs' and things like that * Not so good for PFS0 since there are SHAs list that discourages but good for 'romfs' and things like that
* */ * */
public PipedInputStream getRawDataContentPipedInpStream() throws Exception { public boolean exportMediaBlock(String saveToLocation){
long mediaStartBlocksOffset = ncaHeaderTableEntry.getMediaStartOffset(); File location = new File(saveToLocation);
long mediaEndBlocksOffset = ncaHeaderTableEntry.getMediaEndOffset(); location.mkdirs();
long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset; BufferedInputStream stream;
RandomAccessFile raf = new RandomAccessFile(file, "r"); long mediaStartOffset = ncaHeaderTableEntry.getMediaStartOffset();
///-------------------------------------------------------------------------------------------------- long mediaEndOffset = ncaHeaderTableEntry.getMediaEndOffset();
log.debug("NCAContent() -> exportEncryptedSectionType03() information" + "\n" + long mediaBlocksCount = mediaEndOffset - mediaStartOffset;
"Media start location: " + mediaStartBlocksOffset + "\n" +
"Media end location: " + mediaEndBlocksOffset + "\n" +
"Media size : " + (mediaEndBlocksOffset-mediaStartBlocksOffset) + "\n" +
"Media act. location: " + (ncaOffsetPosition + (mediaStartBlocksOffset * 0x200)) + "\n" +
"KEY: " + Converter.byteArrToHexString(decryptedKey) + "\n" +
"CTR: " + Converter.byteArrToHexString(ncaFsHeader.getSectionCTR()) + "\n");
//---------------------------------------------------------------------------------------------------/
if (ncaFsHeader.getCryptoType() == 0x01){ try (BufferedOutputStream extractedFileBOS = new BufferedOutputStream(
log.trace("NCAContent -> getRawDataContentPipedInpStream (Zero encryption section type 01): Thread started"); Files.newOutputStream(Paths.get(saveToLocation+File.separator+file.getName()+"_MediaBlock.bin")))){
if(ncaFsHeader.getCryptoType()==0x01){
stream = new BufferedInputStream(Files.newInputStream(file.toPath()));
}
else if(ncaFsHeader.getCryptoType()==0x03) {
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey,
ncaFsHeader.getSectionCTR(),
mediaStartOffset * 0x200);
Thread workerThread; stream = new AesCtrBufferedInputStream(decryptor,
PipedOutputStream streamOut = new PipedOutputStream(); ncaOffsetPosition,
mediaStartOffset,
mediaEndOffset,
Files.newInputStream(file.toPath()));
}
else
throw new Exception("Crypto type not supported");
PipedInputStream streamIn = new PipedInputStream(streamOut); for (int i = 0; i < mediaBlocksCount; i++){
workerThread = new Thread(() -> { byte[] block = new byte[0x200];
try { if (0x200 != stream.read(block))
byte[] rawDataBlock; throw new Exception("Read failure");
for (int i = 0; i < mediaBlocksSize; i++){ extractedFileBOS.write(block);
rawDataBlock = new byte[0x200]; }
if (raf.read(rawDataBlock) != -1) stream.close();
streamOut.write(rawDataBlock); } catch (Exception e) {
else log.error("Failed to export MediaBlock", e);
break; return false;
}
}
catch (Exception e){
log.error("NCAContent -> exportRawData() failure", e);
}
finally {
try {
raf.close();
}catch (Exception ignored) {}
try {
streamOut.close();
}catch (Exception ignored) {}
}
log.trace("NCAContent -> exportRawData(): Thread died");
});
workerThread.start();
return streamIn;
} }
else if (ncaFsHeader.getCryptoType() == 0x03){ return true;
log.trace("NCAContent -> getRawDataContentPipedInpStream (Encrypted Section Type 03): Thread started");
if (decryptedKey == null)
throw new Exception("NCAContent -> exportRawData(): unable to proceed. No decrypted key provided.");
Thread workerThread;
PipedOutputStream streamOut = new PipedOutputStream();
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> {
try {
//RandomAccessFile raf = new RandomAccessFile(file, "r");
long abosluteOffsetPosition = ncaOffsetPosition + (mediaStartBlocksOffset * 0x200);
raf.seek(abosluteOffsetPosition);
AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey,
ncaFsHeader.getSectionCTR(),
mediaStartBlocksOffset * 0x200);
byte[] encryptedBlock;
byte[] dectyptedBlock;
// Decrypt data
for (int i = 0; i < mediaBlocksSize; i++){
encryptedBlock = new byte[0x200];
if (raf.read(encryptedBlock) != -1){
dectyptedBlock = decryptor.decryptNext(encryptedBlock);
// Writing decrypted data to pipe
streamOut.write(dectyptedBlock);
}
else
break;
}
}
catch (Exception e){
log.error("NCAContent -> exportRawData(): ", e);
}
finally {
try {
raf.close();
}catch (Exception ignored) {}
try {
streamOut.close();
}catch (Exception ignored) {}
}
log.trace("NCAContent -> exportRawData(): Thread died");
});
workerThread.start();
return streamIn;
}
else
return null;
} }
public long getRawDataContentSize(){ public long getRawDataContentSize(){
return (ncaHeaderTableEntry.getMediaEndOffset() - ncaHeaderTableEntry.getMediaStartOffset()) * 0x200; return (ncaHeaderTableEntry.getMediaEndOffset() - ncaHeaderTableEntry.getMediaStartOffset()) * 0x200;
} }