PFSEncryptedProvider could provide pipe with content data
This commit is contained in:
parent
4116d13f1d
commit
643f09fd9e
13 changed files with 237 additions and 184 deletions
|
@ -44,7 +44,7 @@ public class NCAContentPFS0 {
|
||||||
// Get pfs0
|
// Get pfs0
|
||||||
pfs0 = new PFS0Provider(file, pfs0Location);
|
pfs0 = new PFS0Provider(file, pfs0Location);
|
||||||
}
|
}
|
||||||
// If encrypted (regular)
|
// If encrypted regular [ 0x03 ]
|
||||||
else if (ncaSectionBlock.getCryptoType() == 0x03){
|
else if (ncaSectionBlock.getCryptoType() == 0x03){
|
||||||
new CryptoSection03(file,
|
new CryptoSection03(file,
|
||||||
offsetPosition,
|
offsetPosition,
|
||||||
|
@ -105,7 +105,13 @@ public class NCAContentPFS0 {
|
||||||
streamInp,
|
streamInp,
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize(),
|
||||||
|
offsetPosition,
|
||||||
|
file,
|
||||||
|
decryptedKey,
|
||||||
|
ncaSectionBlock.getSectionCTR(),
|
||||||
|
mediaStartOffset,
|
||||||
|
mediaEndOffset
|
||||||
));
|
));
|
||||||
pThread.start();
|
pThread.start();
|
||||||
// Decrypt data
|
// Decrypt data
|
||||||
|
@ -126,18 +132,6 @@ public class NCAContentPFS0 {
|
||||||
pThread.join();
|
pThread.join();
|
||||||
streamOut.close();
|
streamOut.close();
|
||||||
raf.close();
|
raf.close();
|
||||||
// TODO: re-write
|
|
||||||
if (pfs0 != null){
|
|
||||||
PFS0EncryptedProvider pfs0enc = (PFS0EncryptedProvider)pfs0;
|
|
||||||
pfs0enc.setMeta(
|
|
||||||
offsetPosition,
|
|
||||||
file,
|
|
||||||
decryptedKey,
|
|
||||||
ncaSectionBlock.getSectionCTR(),
|
|
||||||
mediaStartOffset,
|
|
||||||
mediaEndOffset
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//****************************************___DEBUG___*******************************************************
|
//****************************************___DEBUG___*******************************************************
|
||||||
/*
|
/*
|
||||||
File contentFile = new File("/tmp/decryptedNCA0block_"+offsetPosition+".pfs0");
|
File contentFile = new File("/tmp/decryptedNCA0block_"+offsetPosition+".pfs0");
|
||||||
|
@ -180,13 +174,39 @@ public class NCAContentPFS0 {
|
||||||
long hashTableRecordsCount;
|
long hashTableRecordsCount;
|
||||||
long pfs0offset;
|
long pfs0offset;
|
||||||
|
|
||||||
|
private long MetaOffsetPositionInFile;
|
||||||
|
private File MetaFileWithEncPFS0;
|
||||||
|
private byte[] MetaKey;
|
||||||
|
private byte[] MetaSectionCTR;
|
||||||
|
private long MetaMediaStartOffset;
|
||||||
|
private long MetaMediaEndOffset;
|
||||||
|
|
||||||
ParseThread(PipedInputStream pipedInputStream, long pfs0offset, long hashTableOffset, long hashTableSize){
|
|
||||||
|
ParseThread(PipedInputStream pipedInputStream,
|
||||||
|
long pfs0offset,
|
||||||
|
long hashTableOffset,
|
||||||
|
long hashTableSize,
|
||||||
|
|
||||||
|
long MetaOffsetPositionInFile,
|
||||||
|
File MetaFileWithEncPFS0,
|
||||||
|
byte[] MetaKey,
|
||||||
|
byte[] MetaSectionCTR,
|
||||||
|
long MetaMediaStartOffset,
|
||||||
|
long MetaMediaEndOffset
|
||||||
|
){
|
||||||
this.pipedInputStream = pipedInputStream;
|
this.pipedInputStream = pipedInputStream;
|
||||||
this.hashTableOffset = hashTableOffset;
|
this.hashTableOffset = hashTableOffset;
|
||||||
this.hashTableSize = hashTableSize;
|
this.hashTableSize = hashTableSize;
|
||||||
this.hashTableRecordsCount = hashTableSize / 0x20;
|
this.hashTableRecordsCount = hashTableSize / 0x20;
|
||||||
this.pfs0offset = pfs0offset;
|
this.pfs0offset = pfs0offset;
|
||||||
|
|
||||||
|
this.MetaOffsetPositionInFile = MetaOffsetPositionInFile;
|
||||||
|
this.MetaFileWithEncPFS0 = MetaFileWithEncPFS0;
|
||||||
|
this.MetaKey = MetaKey;
|
||||||
|
this.MetaSectionCTR = MetaSectionCTR;
|
||||||
|
this.MetaMediaStartOffset = MetaMediaStartOffset;
|
||||||
|
this.MetaMediaEndOffset = MetaMediaEndOffset;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -223,7 +243,13 @@ public class NCAContentPFS0 {
|
||||||
counter += toSkip;
|
counter += toSkip;
|
||||||
}
|
}
|
||||||
//---------------------------------------------------------
|
//---------------------------------------------------------
|
||||||
pfs0 = new PFS0EncryptedProvider(pipedInputStream, counter);
|
pfs0 = new PFS0EncryptedProvider(pipedInputStream, counter,
|
||||||
|
MetaOffsetPositionInFile,
|
||||||
|
MetaFileWithEncPFS0,
|
||||||
|
MetaKey,
|
||||||
|
MetaSectionCTR,
|
||||||
|
MetaMediaStartOffset,
|
||||||
|
MetaMediaEndOffset);
|
||||||
pipedInputStream.close();
|
pipedInputStream.close();
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
|
|
|
@ -9,7 +9,7 @@ import java.util.Arrays;
|
||||||
import static konogonka.LoperConverter.*;
|
import static konogonka.LoperConverter.*;
|
||||||
|
|
||||||
public class PFS0EncryptedProvider implements IPFS0Provider{
|
public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
private long rawFileDataStart; // For this class is pointing to data start position relative to media block start
|
private long rawFileDataStart; // Always -1 @ PFS0EncryptedProvider
|
||||||
|
|
||||||
private String magic;
|
private String magic;
|
||||||
private int filesCount;
|
private int filesCount;
|
||||||
|
@ -21,138 +21,28 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
|
|
||||||
private long rawBlockDataStart;
|
private long rawBlockDataStart;
|
||||||
|
|
||||||
private PFS0DecryptedStreamProvider pfs0DecryptedStreamProvider;
|
private long offsetPositionInFile;
|
||||||
|
private File file;
|
||||||
// Let's do some fuck
|
private byte[] key;
|
||||||
private class PFS0DecryptedStreamProvider{
|
private byte[] sectionCTR;
|
||||||
private long mediaStartOffset; // * 0x200
|
private long mediaStartOffset;
|
||||||
private long mediaEndOffset;
|
private long mediaEndOffset;
|
||||||
|
|
||||||
|
public PFS0EncryptedProvider(PipedInputStream pipedInputStream, long pfs0offsetPosition,
|
||||||
private RandomAccessFile raf;
|
|
||||||
private PipedOutputStream streamOut;
|
|
||||||
private PipedInputStream streamInp;
|
|
||||||
private AesCtrDecryptSimple aesCtrDecryptSimple; // if null, then exception happened.
|
|
||||||
|
|
||||||
public PipedInputStream getPipedInputStream(){ return streamInp; }
|
|
||||||
|
|
||||||
|
|
||||||
public void getStarted(PFS0subFile subFile) throws Exception{
|
|
||||||
|
|
||||||
System.out.println("rawBlockDataStart (PFS0 Start): "+rawBlockDataStart);
|
|
||||||
System.out.println("Skip blocks: "+rawBlockDataStart/0x200); // aesCtrDecryptSimple.skip(THIS)
|
|
||||||
System.out.println("Skip bytes: "+ (rawBlockDataStart-(rawBlockDataStart/0x200)*0x200)); // write to stream after skiping THIS
|
|
||||||
|
|
||||||
// DBG START
|
|
||||||
File contentFile = new File("/tmp/pfs0-NCA0block.pfs0");
|
|
||||||
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
|
||||||
// DBG END
|
|
||||||
long mediaBlockSize = mediaEndOffset - mediaStartOffset;
|
|
||||||
byte[] encryptedBlock;
|
|
||||||
byte[] dectyptedBlock;
|
|
||||||
|
|
||||||
// Skip full-size blocks of 512 bytes that we don't need and start decryption from required one
|
|
||||||
long startFromBlock = rawBlockDataStart/0x200;
|
|
||||||
if (startFromBlock > 0) {
|
|
||||||
aesCtrDecryptSimple.skipNext(startFromBlock);
|
|
||||||
raf.seek(raf.getFilePointer() + (startFromBlock*0x200));
|
|
||||||
}
|
|
||||||
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left
|
|
||||||
int skipBytes = (int)(rawBlockDataStart-(rawBlockDataStart/0x200)*0x200);
|
|
||||||
if (skipBytes > 0){
|
|
||||||
encryptedBlock = new byte[0x200];
|
|
||||||
if (raf.read(encryptedBlock) != -1){
|
|
||||||
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
|
||||||
try {
|
|
||||||
// DBG START
|
|
||||||
extractedFileOS.write(dectyptedBlock, skipBytes, 0x200-skipBytes);
|
|
||||||
// DBG END
|
|
||||||
//streamOut.write(dectyptedBlock, skipBytes, 0x200);
|
|
||||||
}
|
|
||||||
catch (IOException e){
|
|
||||||
System.out.println("Exception @extract 1st bock");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
startFromBlock++;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (long i = startFromBlock; i < mediaBlockSize; i++){
|
|
||||||
encryptedBlock = new byte[0x200];
|
|
||||||
if (raf.read(encryptedBlock) != -1){
|
|
||||||
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
|
||||||
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
|
||||||
// Writing decrypted data to pipe
|
|
||||||
try {
|
|
||||||
// DBG START
|
|
||||||
extractedFileOS.write(dectyptedBlock);
|
|
||||||
// DBG END
|
|
||||||
//streamOut.write(dectyptedBlock);
|
|
||||||
}
|
|
||||||
catch (IOException e){
|
|
||||||
System.out.println("Exception @extract");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// DBG START
|
|
||||||
extractedFileOS.close();
|
|
||||||
// DBG END
|
|
||||||
}
|
|
||||||
|
|
||||||
PFS0DecryptedStreamProvider(File file,
|
|
||||||
long rawBlockDataStart,
|
|
||||||
long offsetPositionInFile,
|
|
||||||
byte[] key,
|
|
||||||
byte[] sectionCTR,
|
|
||||||
long mediaStartOffset,
|
|
||||||
long mediaEndOffset
|
|
||||||
){
|
|
||||||
this.mediaStartOffset = mediaStartOffset;
|
|
||||||
this.mediaEndOffset = mediaEndOffset;
|
|
||||||
|
|
||||||
try {
|
|
||||||
this.raf = new RandomAccessFile(file, "r");
|
|
||||||
this.raf.seek(offsetPositionInFile + (mediaStartOffset * 0x200));
|
|
||||||
this.aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200);
|
|
||||||
|
|
||||||
this.streamOut = new PipedOutputStream();
|
|
||||||
this.streamInp = new PipedInputStream(streamOut);
|
|
||||||
}
|
|
||||||
catch (Exception e) {
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// TODO: simplify
|
|
||||||
public void setMeta(
|
|
||||||
long offsetPositionInFile,
|
long offsetPositionInFile,
|
||||||
File fileWithEncPFS0,
|
File fileWithEncPFS0,
|
||||||
byte[] key,
|
byte[] key,
|
||||||
byte[] sectionCTR,
|
byte[] sectionCTR,
|
||||||
long mediaStartOffset,
|
long mediaStartOffset,
|
||||||
long mediaEndOffset
|
long mediaEndOffset
|
||||||
){
|
|
||||||
this.pfs0DecryptedStreamProvider = new PFS0DecryptedStreamProvider(
|
|
||||||
fileWithEncPFS0,
|
|
||||||
rawBlockDataStart,
|
|
||||||
offsetPositionInFile,
|
|
||||||
key,
|
|
||||||
sectionCTR,
|
|
||||||
mediaStartOffset,
|
|
||||||
mediaEndOffset
|
|
||||||
);
|
|
||||||
try{
|
|
||||||
pfs0DecryptedStreamProvider.getStarted(pfs0subFiles[0]);
|
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
//---------------------------------------
|
|
||||||
public PFS0EncryptedProvider(PipedInputStream pipedInputStream,
|
|
||||||
long pfs0offsetPosition
|
|
||||||
) throws Exception{
|
) throws Exception{
|
||||||
|
// Populate 'meta' data that is needed for getProviderSubFilePipedInpStream()
|
||||||
|
this.offsetPositionInFile = offsetPositionInFile;
|
||||||
|
this.file = fileWithEncPFS0;
|
||||||
|
this.key = key;
|
||||||
|
this.sectionCTR = sectionCTR;
|
||||||
|
this.mediaStartOffset = mediaStartOffset;
|
||||||
|
this.mediaEndOffset = mediaEndOffset;
|
||||||
// pfs0offsetPosition is a position relative to Media block. Lets add pfs0 'header's' bytes count and get raw data start position in media block
|
// pfs0offsetPosition is a position relative to Media block. Lets add pfs0 'header's' bytes count and get raw data start position in media block
|
||||||
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
|
rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider
|
||||||
// Detect raw data start position using next var
|
// Detect raw data start position using next var
|
||||||
|
@ -164,7 +54,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
for (int i = 0; i < 0x10; i++){
|
for (int i = 0; i < 0x10; i++){
|
||||||
int currentByte = pipedInputStream.read();
|
int currentByte = pipedInputStream.read();
|
||||||
if (currentByte == -1) {
|
if (currentByte == -1) {
|
||||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read starting 0x10 bytes");
|
throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read starting 0x10 bytes");
|
||||||
}
|
}
|
||||||
fileStartingBytes[i] = (byte)currentByte;
|
fileStartingBytes[i] = (byte)currentByte;
|
||||||
}
|
}
|
||||||
|
@ -173,17 +63,17 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
// Check PFS0Provider
|
// Check PFS0Provider
|
||||||
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
||||||
if (! magic.equals("PFS0")){
|
if (! magic.equals("PFS0")){
|
||||||
throw new Exception("PFS0Provider: Bad magic");
|
throw new Exception("PFS0EncryptedProvider: Bad magic");
|
||||||
}
|
}
|
||||||
// Get files count
|
// Get files count
|
||||||
filesCount = getLEint(fileStartingBytes, 0x4);
|
filesCount = getLEint(fileStartingBytes, 0x4);
|
||||||
if (filesCount <= 0 ) {
|
if (filesCount <= 0 ) {
|
||||||
throw new Exception("PFS0Provider: Files count is too small");
|
throw new Exception("PFS0EncryptedProvider: Files count is too small");
|
||||||
}
|
}
|
||||||
// Get string table
|
// Get string table
|
||||||
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
||||||
if (stringTableSize <= 0 ){
|
if (stringTableSize <= 0 ){
|
||||||
throw new Exception("PFS0Provider: String table is too small");
|
throw new Exception("PFS0EncryptedProvider: String table is too small");
|
||||||
}
|
}
|
||||||
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
||||||
//---------------------------------------------------------------------------------------------------------
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
@ -199,7 +89,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
for (int j = 0; j < 0x18; j++){
|
for (int j = 0; j < 0x18; j++){
|
||||||
int currentByte = pipedInputStream.read();
|
int currentByte = pipedInputStream.read();
|
||||||
if (currentByte == -1) {
|
if (currentByte == -1) {
|
||||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read File Entry Table #"+i);
|
throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read File Entry Table #"+i);
|
||||||
}
|
}
|
||||||
fileEntryTable[j] = (byte)currentByte;
|
fileEntryTable[j] = (byte)currentByte;
|
||||||
}
|
}
|
||||||
|
@ -218,7 +108,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
for (int i = 0; i < stringTableSize; i++){
|
for (int i = 0; i < stringTableSize; i++){
|
||||||
int currentByte = pipedInputStream.read();
|
int currentByte = pipedInputStream.read();
|
||||||
if (currentByte == -1) {
|
if (currentByte == -1) {
|
||||||
throw new Exception("PFS0: Reading stream suddenly ended while trying to read string table");
|
throw new Exception("PFS0EncryptedProvider: Reading stream suddenly ended while trying to read string table");
|
||||||
}
|
}
|
||||||
stringTbl[i] = (byte)currentByte;
|
stringTbl[i] = (byte)currentByte;
|
||||||
}
|
}
|
||||||
|
@ -239,7 +129,6 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
zeroBytes[i]
|
zeroBytes[i]
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -258,14 +147,147 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) {
|
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) {
|
||||||
//TODO
|
if (subFileNumber >= pfs0subFiles.length) {
|
||||||
|
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
/*/------------------------------------------------------------------
|
||||||
|
System.out.println("Raw Block Data Start (PFS0 Start): " + rawBlockDataStart);
|
||||||
|
System.out.println("Skipped blocks: " + rawBlockDataStart/0x200); // aesCtrDecryptSimple.skip(THIS)
|
||||||
|
System.out.println("Skip bytes: " + (rawBlockDataStart-(rawBlockDataStart/0x200)*0x200)); // write to stream after skiping THIS
|
||||||
|
*///-----------------------------------------------------------------
|
||||||
|
Thread workerThread;
|
||||||
|
PipedOutputStream streamOut = new PipedOutputStream();
|
||||||
|
|
||||||
|
try{
|
||||||
|
PipedInputStream streamIn = new PipedInputStream(streamOut);
|
||||||
|
workerThread = new Thread(() -> {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Executing thread");
|
||||||
|
try {
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
||||||
|
// Let's store what we're about to skip
|
||||||
|
int skipBytes = (int) (offsetPositionInFile + (mediaStartOffset * 0x200));
|
||||||
|
// Check if skip was successful
|
||||||
|
if (bis.skip(skipBytes) != skipBytes) {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
AesCtrDecryptSimple aesCtrDecryptSimple = new AesCtrDecryptSimple(key, sectionCTR, mediaStartOffset * 0x200);
|
||||||
|
|
||||||
|
byte[] encryptedBlock;
|
||||||
|
byte[] dectyptedBlock;
|
||||||
|
|
||||||
|
//----------------------------- Pre-set: skip non-necessary data --------------------------------
|
||||||
|
|
||||||
|
long startBlock = (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) / 0x200; // <- pointing to place where actual data starts
|
||||||
|
|
||||||
|
if (startBlock > 0) {
|
||||||
|
aesCtrDecryptSimple.skipNext(startBlock);
|
||||||
|
skipBytes = (int)(startBlock * 0x200);
|
||||||
|
if (bis.skip(skipBytes) != skipBytes) {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Failed to skip range "+skipBytes);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//----------------------------- Step 1: get starting bytes from the end of the junk block --------------------------------
|
||||||
|
|
||||||
|
// Since our data could be located in position with some offset from the decrypted block, let's skip bytes left. Considering the case when data is not aligned to block
|
||||||
|
skipBytes = (int) ( (rawBlockDataStart + pfs0subFiles[subFileNumber].getOffset()) - startBlock * 0x200); // <- How much bytes shall we skip to reach requested data start of sub-file
|
||||||
|
|
||||||
|
if (skipBytes > 0) {
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (bis.read(encryptedBlock) == 0x200) {
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
// If we have extra-small file that is less then a block and even more
|
||||||
|
if ((0x200 - skipBytes) > pfs0subFiles[subFileNumber].getSize()){
|
||||||
|
streamOut.write(dectyptedBlock, skipBytes, (int) pfs0subFiles[subFileNumber].getSize()); // safe cast
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
streamOut.write(dectyptedBlock, skipBytes, 0x200 - skipBytes);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startBlock++;
|
||||||
|
}
|
||||||
|
|
||||||
|
long endBlock = pfs0subFiles[subFileNumber].getSize() / 0x200 + startBlock; // <- pointing to place where any data related to this media-block ends
|
||||||
|
|
||||||
|
//----------------------------- Step 2: Detect if we have junk data on the end of the final block --------------------------------
|
||||||
|
int extraData = (int)(rawBlockDataStart+pfs0subFiles[subFileNumber].getOffset()+pfs0subFiles[subFileNumber].getSize() - (endBlock*0x200)); // safe cast
|
||||||
|
if (extraData < 0){
|
||||||
|
endBlock--;
|
||||||
|
}
|
||||||
|
//----------------------------- Step 3: Read main part of data --------------------------------
|
||||||
|
// Here we're reading main amount of bytes. We can read only less bytes.
|
||||||
|
while ( startBlock < endBlock) {
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (bis.read(encryptedBlock) == 0x200) {
|
||||||
|
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
// Writing decrypted data to pipe
|
||||||
|
streamOut.write(dectyptedBlock);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
startBlock++;
|
||||||
|
}
|
||||||
|
//----------------------------- Step 4: Read what's left --------------------------------
|
||||||
|
// Now we have to find out if data overlaps to one more extra block
|
||||||
|
if (extraData > 0){ // In case we didn't get what we want
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (bis.read(encryptedBlock) == 0x200) {
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
streamOut.write(dectyptedBlock, 0, extraData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (extraData < 0){ // In case we can get more than we need
|
||||||
|
encryptedBlock = new byte[0x200];
|
||||||
|
if (bis.read(encryptedBlock) == 0x200) {
|
||||||
|
dectyptedBlock = aesCtrDecryptSimple.dectyptNext(encryptedBlock);
|
||||||
|
streamOut.write(dectyptedBlock, 0, 0x200 + extraData);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
bis.close();
|
||||||
|
streamOut.close();
|
||||||
|
}
|
||||||
|
catch (Exception e){
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): "+e.getMessage());
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Thread died");
|
||||||
|
|
||||||
|
|
||||||
|
});
|
||||||
|
workerThread.start();
|
||||||
|
return streamIn;
|
||||||
|
}
|
||||||
|
catch (IOException ioe){
|
||||||
|
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@Override
|
@Override
|
||||||
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) {
|
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) {
|
||||||
//TODO
|
for (int i = 0; i < pfs0subFiles.length; i++){
|
||||||
|
if (pfs0subFiles[i].getName().equals(subFileName))
|
||||||
|
return getProviderSubFilePipedInpStream(i);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ import java.util.Arrays;
|
||||||
import static konogonka.LoperConverter.*;
|
import static konogonka.LoperConverter.*;
|
||||||
|
|
||||||
public class PFS0Provider implements IPFS0Provider{
|
public class PFS0Provider implements IPFS0Provider{
|
||||||
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
private long rawFileDataStart; // Where data starts, excluding header, string table etc.
|
||||||
|
|
||||||
private String magic;
|
private String magic;
|
||||||
private int filesCount;
|
private int filesCount;
|
||||||
|
|
|
@ -54,6 +54,11 @@ public class NspXciExtractor extends Task<Void> {
|
||||||
}
|
}
|
||||||
//*** PROGRESS BAR DECORCATIONS END
|
//*** PROGRESS BAR DECORCATIONS END
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
|
logPrinter.updateProgress(1.0);
|
||||||
|
}catch (InterruptedException ie){
|
||||||
|
getException().printStackTrace(); // TODO: Do something with this
|
||||||
|
}
|
||||||
extractedFileBOS.close();
|
extractedFileBOS.close();
|
||||||
} catch (IOException ioe) {
|
} catch (IOException ioe) {
|
||||||
logPrinter.print("\tRead/Write error\n\t" + ioe.getMessage(), EMsgType.INFO);
|
logPrinter.print("\tRead/Write error\n\t" + ioe.getMessage(), EMsgType.INFO);
|
||||||
|
|
|
@ -34,7 +34,7 @@ import java.util.function.LongFunction;
|
||||||
* XTS-AES cipher with arbitrary (non 128-bit aligned) data unit lengths.
|
* XTS-AES cipher with arbitrary (non 128-bit aligned) data unit lengths.
|
||||||
*
|
*
|
||||||
* @author Ahseya
|
* @author Ahseya
|
||||||
* @author Dmitry Isaenko (updates for special usage)
|
* (updates for special usage by Dmitry Isaenko)
|
||||||
*/
|
*/
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
public class XTSAESCipher {
|
public class XTSAESCipher {
|
||||||
|
|
|
@ -36,7 +36,7 @@ import java.util.Objects;
|
||||||
* XTS core functions.
|
* XTS core functions.
|
||||||
*
|
*
|
||||||
* @author Ahseya
|
* @author Ahseya
|
||||||
* @author Dmitry Isaenko (updates for special usage)
|
* (updates for special usage by Dmitry Isaenko)
|
||||||
*/
|
*/
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
class XTSCore {
|
class XTSCore {
|
||||||
|
|
|
@ -38,7 +38,7 @@ import java.util.function.LongFunction;
|
||||||
* XTS tweak with pluggable tweak function.
|
* XTS tweak with pluggable tweak function.
|
||||||
*
|
*
|
||||||
* @author Ahseya
|
* @author Ahseya
|
||||||
* @author Dmitry Isaenko (updates for special usage)
|
* (updates for special usage by Dmitry Isaenko)
|
||||||
*/
|
*/
|
||||||
@NotThreadSafe
|
@NotThreadSafe
|
||||||
class XTSTweak {
|
class XTSTweak {
|
||||||
|
|
|
@ -3,9 +3,9 @@ btnAnalyze=Analyze
|
||||||
lblNoFileSelected=No files selected.
|
lblNoFileSelected=No files selected.
|
||||||
tableNumberLbl=#
|
tableNumberLbl=#
|
||||||
tableFileNameLbl=File name
|
tableFileNameLbl=File name
|
||||||
tableSizeLbl=Size (kB)
|
tableSizeLbl=Size (b)
|
||||||
tableUploadLbl=Extract?
|
tableUploadLbl=Extract?
|
||||||
tableOffsetLbl=Offset (kB)
|
tableOffsetLbl=Offset (B)
|
||||||
btnExtract=Extract files
|
btnExtract=Extract files
|
||||||
btnLogShow=Log
|
btnLogShow=Log
|
||||||
settings_SettingsName=Settings
|
settings_SettingsName=Settings
|
||||||
|
|
|
@ -2,10 +2,10 @@ btnFileOpen=Selectionner NSP/XCI/NCA
|
||||||
btnAnalyze=Analyser
|
btnAnalyze=Analyser
|
||||||
lblNoFileSelected=Aucuns fichiers s\u00E9lectionn\u00E9s.
|
lblNoFileSelected=Aucuns fichiers s\u00E9lectionn\u00E9s.
|
||||||
tableUploadLbl=Extraire ?
|
tableUploadLbl=Extraire ?
|
||||||
tableSizeLbl=Taille (kB)
|
tableSizeLbl=Taille (b)
|
||||||
tableFileNameLbl=Nom de fichier
|
tableFileNameLbl=Nom de fichier
|
||||||
tableNumberLbl=#
|
tableNumberLbl=#
|
||||||
tableOffsetLbl=D\u00E9calage (kB)
|
tableOffsetLbl=D\u00E9calage (B)
|
||||||
btnExtract=Extraire les fichiers
|
btnExtract=Extraire les fichiers
|
||||||
btnLogShow=B\u00FBche
|
btnLogShow=B\u00FBche
|
||||||
settings_SettingsName=R\u00E9glages
|
settings_SettingsName=R\u00E9glages
|
||||||
|
|
|
@ -2,10 +2,10 @@ btnFileOpen=\u0412\u044B\u0431\u0440\u0430\u0442\u044C NSP/XCI/NCA
|
||||||
btnAnalyze=\u0410\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C
|
btnAnalyze=\u0410\u043D\u0430\u043B\u0438\u0437\u0438\u0440\u043E\u0432\u0430\u0442\u044C
|
||||||
lblNoFileSelected=\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
|
lblNoFileSelected=\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B.
|
||||||
tableUploadLbl=\u0420\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u0442\u044C?
|
tableUploadLbl=\u0420\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u0442\u044C?
|
||||||
tableSizeLbl=\u0420\u0430\u0437\u043C\u0435\u0440 (\u043A\u0411)
|
tableSizeLbl=\u0420\u0430\u0437\u043C\u0435\u0440 (\u0411)
|
||||||
tableFileNameLbl=\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430
|
tableFileNameLbl=\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430
|
||||||
tableNumberLbl=\u2116
|
tableNumberLbl=\u2116
|
||||||
tableOffsetLbl=\u0421\u043C\u0435\u0449\u0435\u043D\u0438\u0435 (\u043A\u0411)
|
tableOffsetLbl=\u0421\u043C\u0435\u0449\u0435\u043D\u0438\u0435 (\u0411)
|
||||||
btnExtract=\u0420\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u0442\u044C \u0444\u0430\u0439\u043B\u044B
|
btnExtract=\u0420\u0430\u0441\u043F\u0430\u043A\u043E\u0432\u0430\u0442\u044C \u0444\u0430\u0439\u043B\u044B
|
||||||
btnLogShow=\u041B\u043E\u0433
|
btnLogShow=\u041B\u043E\u0433
|
||||||
settings_SettingsName=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438
|
settings_SettingsName=\u041D\u0430\u0441\u0442\u0440\u043E\u0439\u043A\u0438
|
||||||
|
|
|
@ -3,9 +3,9 @@ btnAnalyze=\u0410\u043D\u0430\u043B\u0456\u0437\u0443\u0432\u0430\u0442\u0438
|
||||||
lblNoFileSelected=\u0424\u0430\u0439\u043B\u0438 \u043D\u0435 \u0432\u0438\u0431\u0440\u0430\u043D\u0456.
|
lblNoFileSelected=\u0424\u0430\u0439\u043B\u0438 \u043D\u0435 \u0432\u0438\u0431\u0440\u0430\u043D\u0456.
|
||||||
tableNumberLbl=\u2116
|
tableNumberLbl=\u2116
|
||||||
tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443
|
tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443
|
||||||
tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 (\u043A\u0411)
|
tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 (\u0411)
|
||||||
tableUploadLbl=\u0420\u043E\u0437\u043F\u0430\u043A\u043E\u0432\u0443\u0432\u0430\u0442\u0438?
|
tableUploadLbl=\u0420\u043E\u0437\u043F\u0430\u043A\u043E\u0432\u0443\u0432\u0430\u0442\u0438?
|
||||||
tableOffsetLbl=\u0417\u043C\u0456\u0449\u0435\u043D\u043D\u044F (\u043A\u0411)
|
tableOffsetLbl=\u0417\u043C\u0456\u0449\u0435\u043D\u043D\u044F (\u0411)
|
||||||
btnExtract=\u0420\u043E\u0437\u043F\u0430\u043A\u0443\u0432\u0430\u0442\u0438 \u0444\u0430\u0439\u043B\u0438
|
btnExtract=\u0420\u043E\u0437\u043F\u0430\u043A\u0443\u0432\u0430\u0442\u0438 \u0444\u0430\u0439\u043B\u0438
|
||||||
btnLogShow=\u041B\u043E\u0433
|
btnLogShow=\u041B\u043E\u0433
|
||||||
settings_SettingsName=\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F
|
settings_SettingsName=\u041D\u0430\u043B\u0430\u0448\u0442\u0443\u0432\u0430\u043D\u043D\u044F
|
||||||
|
|
|
@ -17,7 +17,7 @@
|
||||||
-fx-border-color: #0d98ba;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -30,7 +30,7 @@
|
||||||
-fx-border-color: #959595;
|
-fx-border-color: #959595;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #bbbbbb;
|
-fx-text-fill: #bbbbbb;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,7 +39,7 @@
|
||||||
-fx-border-color: #0d98ba;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
.button:focused, .choice-box:focused{
|
.button:focused, .choice-box:focused{
|
||||||
|
@ -47,7 +47,7 @@
|
||||||
-fx-border-color: #0d98ba;
|
-fx-border-color: #0d98ba;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
-fx-border-color: #007cad;
|
-fx-border-color: #007cad;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
// -========================+ Button RED =====================-
|
// -========================+ Button RED =====================-
|
||||||
|
@ -68,7 +68,7 @@
|
||||||
-fx-border-color: #e34234;
|
-fx-border-color: #e34234;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@
|
||||||
-fx-border-color: #e34234;
|
-fx-border-color: #e34234;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
.buttonRed:focused{
|
.buttonRed:focused{
|
||||||
|
@ -85,7 +85,7 @@
|
||||||
-fx-border-color: #cc0605;
|
-fx-border-color: #cc0605;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #2c2c2c;
|
-fx-text-fill: #2c2c2c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
-fx-border-color: #cc0605;
|
-fx-border-color: #cc0605;
|
||||||
-fx-background-radius: 5;
|
-fx-background-radius: 5;
|
||||||
-fx-border-radius: 5;
|
-fx-border-radius: 5;
|
||||||
-fx-border-width: 2;
|
-fx-border-width: 1;
|
||||||
-fx-text-fill: #ffffff;
|
-fx-text-fill: #ffffff;
|
||||||
}
|
}
|
||||||
// -========================+ TextArea =====================-
|
// -========================+ TextArea =====================-
|
||||||
|
|
Loading…
Reference in a new issue