start working on encrypted sections extractor
This commit is contained in:
parent
e2f7f93988
commit
dd6c81c7fd
8 changed files with 276 additions and 155 deletions
|
@ -7,6 +7,7 @@ import javafx.scene.layout.HBox;
|
||||||
import javafx.scene.layout.VBox;
|
import javafx.scene.layout.VBox;
|
||||||
import konogonka.Controllers.NSP.NSPController;
|
import konogonka.Controllers.NSP.NSPController;
|
||||||
import konogonka.LoperConverter;
|
import konogonka.LoperConverter;
|
||||||
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
@ -23,7 +24,7 @@ public class NCASectionContentController{
|
||||||
sha256pane.getChildren().clear();
|
sha256pane.getChildren().clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void populateFields(PFS0Provider pfs0, File file, LinkedList<byte[]> sha256hashList) {
|
public void populateFields(IPFS0Provider pfs0, File file, LinkedList<byte[]> sha256hashList) {
|
||||||
resetTab();
|
resetTab();
|
||||||
SectionPFS0Controller.setData(pfs0, file);
|
SectionPFS0Controller.setData(pfs0, file);
|
||||||
if (sha256hashList != null){
|
if (sha256hashList != null){
|
||||||
|
|
|
@ -6,6 +6,7 @@ import javafx.scene.control.Label;
|
||||||
import konogonka.Controllers.IRowModel;
|
import konogonka.Controllers.IRowModel;
|
||||||
import konogonka.Controllers.TabController;
|
import konogonka.Controllers.TabController;
|
||||||
import konogonka.MediatorControl;
|
import konogonka.MediatorControl;
|
||||||
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
import konogonka.Workers.AnalyzerNSP;
|
import konogonka.Workers.AnalyzerNSP;
|
||||||
import konogonka.Workers.NspXciExtractor;
|
import konogonka.Workers.NspXciExtractor;
|
||||||
|
@ -102,7 +103,7 @@ public class NSPController implements TabController {
|
||||||
/**
|
/**
|
||||||
* Just populate fields by already analyzed PFS0
|
* Just populate fields by already analyzed PFS0
|
||||||
* */
|
* */
|
||||||
public void setData(PFS0Provider pfs0, File fileWithNca){
|
public void setData(IPFS0Provider pfs0, File fileWithNca){
|
||||||
if (pfs0 != null){
|
if (pfs0 != null){
|
||||||
if (fileWithNca != null)
|
if (fileWithNca != null)
|
||||||
this.selectedFile = fileWithNca;
|
this.selectedFile = fileWithNca;
|
||||||
|
|
|
@ -17,6 +17,7 @@ import javafx.scene.input.MouseButton;
|
||||||
import javafx.scene.input.MouseEvent;
|
import javafx.scene.input.MouseEvent;
|
||||||
import javafx.util.Callback;
|
import javafx.util.Callback;
|
||||||
import konogonka.Controllers.IRowModel;
|
import konogonka.Controllers.IRowModel;
|
||||||
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -144,7 +145,7 @@ public class Pfs0TableViewController implements Initializable {
|
||||||
/**
|
/**
|
||||||
* Add files when user selected them
|
* Add files when user selected them
|
||||||
* */
|
* */
|
||||||
public void setNSPToTable(PFS0Provider pfs){
|
public void setNSPToTable(IPFS0Provider pfs){
|
||||||
rowsObsLst.clear();
|
rowsObsLst.clear();
|
||||||
Pfs0RowModel.resetNumCnt();
|
Pfs0RowModel.resetNumCnt();
|
||||||
if (pfs == null) {
|
if (pfs == null) {
|
||||||
|
|
|
@ -3,6 +3,8 @@ package konogonka.Tools.NCA;
|
||||||
import konogonka.LoperConverter;
|
import konogonka.LoperConverter;
|
||||||
import konogonka.RainbowHexDump;
|
import konogonka.RainbowHexDump;
|
||||||
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
|
import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock;
|
||||||
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
|
import konogonka.Tools.PFS0.PFS0EncryptedProvider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
import konogonka.ctraes.AesCtr;
|
import konogonka.ctraes.AesCtr;
|
||||||
|
|
||||||
|
@ -11,66 +13,68 @@ import java.util.LinkedList;
|
||||||
|
|
||||||
public class NCAContentPFS0 {
|
public class NCAContentPFS0 {
|
||||||
private LinkedList<byte[]> SHA256hashes;
|
private LinkedList<byte[]> SHA256hashes;
|
||||||
private PFS0Provider pfs0;
|
private IPFS0Provider pfs0;
|
||||||
|
|
||||||
// TODO: if decryptedKey is empty, thorow exception ??
|
// TODO: if decryptedKey is empty, thorow exception ??
|
||||||
public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){
|
public NCAContentPFS0(File file, long offsetPosition, NCASectionBlock ncaSectionBlock, NCAHeaderTableEntry ncaHeaderTableEntry, byte[] decryptedKey){
|
||||||
SHA256hashes = new LinkedList<>();
|
SHA256hashes = new LinkedList<>();
|
||||||
try {
|
|
||||||
// If it's PFS0Provider
|
// If it's PFS0Provider
|
||||||
if (ncaSectionBlock.getSuperBlockPFS0() != null){
|
if (ncaSectionBlock.getSuperBlockPFS0() != null){
|
||||||
// IF NO ENCRYPTION
|
try {
|
||||||
if (ncaSectionBlock.getCryptoType() == 0x1) {
|
// IF NO ENCRYPTION
|
||||||
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
if (ncaSectionBlock.getCryptoType() == 0x1) {
|
||||||
long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200);
|
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||||
long hashTableLocation = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset();
|
long thisMediaLocation = offsetPosition + (ncaHeaderTableEntry.getMediaStartOffset() * 0x200);
|
||||||
long pfs0Location = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset();
|
long hashTableLocation = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset();
|
||||||
|
long pfs0Location = thisMediaLocation + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset();
|
||||||
|
|
||||||
raf.seek(hashTableLocation);
|
raf.seek(hashTableLocation);
|
||||||
|
|
||||||
byte[] rawData;
|
byte[] rawData;
|
||||||
long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20;
|
long sha256recordsNumber = ncaSectionBlock.getSuperBlockPFS0().getHashTableSize() / 0x20;
|
||||||
// Collect hashes
|
// Collect hashes
|
||||||
for (int i = 0; i < sha256recordsNumber; i++){
|
for (int i = 0; i < sha256recordsNumber; i++){
|
||||||
rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash
|
rawData = new byte[0x20]; // 32 bytes - size of SHA256 hash
|
||||||
if (raf.read(rawData) != -1)
|
if (raf.read(rawData) != -1)
|
||||||
SHA256hashes.add(rawData);
|
SHA256hashes.add(rawData);
|
||||||
else
|
else {
|
||||||
return; // TODO: fix
|
raf.close();
|
||||||
|
return; // TODO: fix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
raf.close();
|
||||||
|
// Get pfs0
|
||||||
|
pfs0 = new PFS0Provider(file, pfs0Location);
|
||||||
|
}
|
||||||
|
// If encrypted (regular)
|
||||||
|
else if (ncaSectionBlock.getCryptoType() == 0x3){
|
||||||
|
new CryptoSection03(file,
|
||||||
|
offsetPosition,
|
||||||
|
decryptedKey,
|
||||||
|
ncaSectionBlock,
|
||||||
|
ncaHeaderTableEntry.getMediaStartOffset(),
|
||||||
|
ncaHeaderTableEntry.getMediaEndOffset());
|
||||||
}
|
}
|
||||||
raf.close();
|
|
||||||
// Get pfs0
|
|
||||||
pfs0 = new PFS0Provider(file, pfs0Location);
|
|
||||||
}
|
}
|
||||||
// If encrypted (regular)
|
catch (Exception e){
|
||||||
else if (ncaSectionBlock.getCryptoType() == 0x3){ // d0c1...
|
e.printStackTrace();
|
||||||
new CryptoSection03(file,
|
|
||||||
offsetPosition,
|
|
||||||
decryptedKey,
|
|
||||||
ncaSectionBlock,
|
|
||||||
ncaHeaderTableEntry.getMediaStartOffset(),
|
|
||||||
ncaHeaderTableEntry.getMediaEndOffset());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (ncaSectionBlock.getSuperBlockIVFC() != null){
|
else if (ncaSectionBlock.getSuperBlockIVFC() != null){
|
||||||
|
// TODO
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return; // TODO: FIX THIS STUFF
|
return; // TODO: FIX THIS STUFF
|
||||||
}
|
}
|
||||||
}
|
|
||||||
catch (Exception e){
|
|
||||||
e.printStackTrace();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public LinkedList<byte[]> getSHA256hashes() { return SHA256hashes; }
|
public LinkedList<byte[]> getSHA256hashes() { return SHA256hashes; }
|
||||||
public PFS0Provider getPfs0() { return pfs0; }
|
public IPFS0Provider getPfs0() { return pfs0; }
|
||||||
|
|
||||||
private class CryptoSection03{
|
private class CryptoSection03{
|
||||||
|
|
||||||
CryptoSection03(File file, long offsetPosition, byte[] decryptedKey, NCASectionBlock ncaSectionBlock, long mediaStartOffset, long mediaEndOffset) throws Exception{
|
CryptoSection03(File file, long offsetPosition, byte[] decryptedKey, NCASectionBlock ncaSectionBlock, long mediaStartOffset, long mediaEndOffset) throws Exception{
|
||||||
//--------------------------------------------------------------------------------------------------
|
/*//--------------------------------------------------------------------------------------------------
|
||||||
System.out.println("Media start location: " + mediaStartOffset);
|
System.out.println("Media start location: " + mediaStartOffset);
|
||||||
System.out.println("Media end location: " + mediaEndOffset);
|
System.out.println("Media end location: " + mediaEndOffset);
|
||||||
System.out.println("Media size : " + (mediaEndOffset-mediaStartOffset));
|
System.out.println("Media size : " + (mediaEndOffset-mediaStartOffset));
|
||||||
|
@ -82,13 +86,13 @@ public class NCAContentPFS0 {
|
||||||
System.out.println("KEY: " + LoperConverter.byteArrToHexString(decryptedKey));
|
System.out.println("KEY: " + LoperConverter.byteArrToHexString(decryptedKey));
|
||||||
System.out.println("CTR: " + LoperConverter.byteArrToHexString(ncaSectionBlock.getSectionCTR()));
|
System.out.println("CTR: " + LoperConverter.byteArrToHexString(ncaSectionBlock.getSectionCTR()));
|
||||||
System.out.println();
|
System.out.println();
|
||||||
//--------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------*/
|
||||||
|
|
||||||
if (decryptedKey == null)
|
if (decryptedKey == null)
|
||||||
throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided.");
|
throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided.");
|
||||||
|
|
||||||
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
RandomAccessFile raf = new RandomAccessFile(file, "r");
|
||||||
raf.seek(offsetPosition + (mediaStartOffset * 0x200));
|
long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200);
|
||||||
|
raf.seek(abosluteOffsetPosition);
|
||||||
|
|
||||||
AesCtrDecryptor decryptor = new AesCtrDecryptor(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
AesCtrDecryptor decryptor = new AesCtrDecryptor(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200);
|
||||||
|
|
||||||
|
@ -99,13 +103,14 @@ public class NCAContentPFS0 {
|
||||||
PipedOutputStream streamOut = new PipedOutputStream();
|
PipedOutputStream streamOut = new PipedOutputStream();
|
||||||
PipedInputStream streamInp = new PipedInputStream(streamOut);
|
PipedInputStream streamInp = new PipedInputStream(streamOut);
|
||||||
|
|
||||||
new Thread(new ParseThread(
|
Thread pThread = new Thread(new ParseThread(
|
||||||
streamInp,
|
streamInp,
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
ncaSectionBlock.getSuperBlockPFS0().getPfs0offset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getPfs0size(),
|
ncaSectionBlock.getSuperBlockPFS0().getPfs0size(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset(),
|
||||||
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()
|
||||||
)).start();
|
));
|
||||||
|
pThread.start();
|
||||||
// Decrypt data
|
// Decrypt data
|
||||||
for (int i = 0; i < mediaBlockSize; i++){
|
for (int i = 0; i < mediaBlockSize; i++){
|
||||||
encryptedBlock = new byte[0x200];
|
encryptedBlock = new byte[0x200];
|
||||||
|
@ -113,12 +118,16 @@ public class NCAContentPFS0 {
|
||||||
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
//dectyptedBlock = aesCtr.decrypt(encryptedBlock);
|
||||||
dectyptedBlock = decryptor.dectyptNext(encryptedBlock);
|
dectyptedBlock = decryptor.dectyptNext(encryptedBlock);
|
||||||
// Writing decrypted data to pipe
|
// Writing decrypted data to pipe
|
||||||
streamOut.write(dectyptedBlock);
|
try {
|
||||||
|
streamOut.write(dectyptedBlock);
|
||||||
|
}
|
||||||
|
catch (IOException e){
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
streamOut.flush();
|
pThread.join();
|
||||||
streamOut.close();
|
streamOut.close();
|
||||||
|
|
||||||
raf.close();
|
raf.close();
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
@ -187,51 +196,39 @@ public class NCAContentPFS0 {
|
||||||
return; // TODO: fix?
|
return; // TODO: fix?
|
||||||
counter = hashTableOffset;
|
counter = hashTableOffset;
|
||||||
}
|
}
|
||||||
// Main loop
|
// Loop for collecting all recrods from sha256 hash table
|
||||||
while (true){
|
while ((counter - hashTableOffset) < hashTableSize){
|
||||||
// Loop for collecting all recrods from sha256 hash table
|
int hashCounter = 0;
|
||||||
while ((counter - hashTableOffset) < hashTableSize){
|
byte[] sectionHash = new byte[0x20];
|
||||||
int hashCounter = 0;
|
// Loop for collecting bytes for every SINGLE records, where record size == 0x20
|
||||||
byte[] sectionHash = new byte[0x20];
|
while (hashCounter < 0x20){
|
||||||
// Loop for collecting bytes for every SINGLE records, where record size == 0x20
|
|
||||||
while (hashCounter < 0x20){
|
|
||||||
int currentByte = pipedInputStream.read();
|
|
||||||
if (currentByte == -1)
|
|
||||||
break;
|
|
||||||
sectionHash[hashCounter] = (byte)currentByte;
|
|
||||||
hashCounter++;
|
|
||||||
counter++;
|
|
||||||
}
|
|
||||||
// Write after collecting
|
|
||||||
SHA256hashes.add(sectionHash); // From the NCAContentProvider obviously
|
|
||||||
}
|
|
||||||
// Skip padding and go to PFS0 location
|
|
||||||
if (counter < pfs0offset){
|
|
||||||
long toSkip = pfs0offset-counter;
|
|
||||||
if (toSkip != pipedInputStream.skip(toSkip))
|
|
||||||
return; // TODO: fix?
|
|
||||||
counter += toSkip;
|
|
||||||
}
|
|
||||||
//---------------------------------------------------------
|
|
||||||
byte[] magic = new byte[0x4];
|
|
||||||
for (int i = 0; i < 4; i++){
|
|
||||||
int currentByte = pipedInputStream.read();
|
int currentByte = pipedInputStream.read();
|
||||||
if (currentByte == -1)
|
if (currentByte == -1)
|
||||||
break;
|
break;
|
||||||
magic[i] = (byte)currentByte;
|
sectionHash[hashCounter] = (byte)currentByte;
|
||||||
|
hashCounter++;
|
||||||
|
counter++;
|
||||||
}
|
}
|
||||||
RainbowHexDump.hexDumpUTF8(magic);
|
// Write after collecting
|
||||||
while (pipedInputStream.read() != -1)
|
SHA256hashes.add(sectionHash); // From the NCAContentProvider obviously
|
||||||
;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
// Skip padding and go to PFS0 location
|
||||||
|
if (counter < pfs0offset){
|
||||||
|
long toSkip = pfs0offset-counter;
|
||||||
|
if (toSkip != pipedInputStream.skip(toSkip))
|
||||||
|
return; // TODO: fix?
|
||||||
|
counter += toSkip;
|
||||||
|
}
|
||||||
|
//---------------------------------------------------------
|
||||||
|
pfs0 = new PFS0EncryptedProvider(pipedInputStream);
|
||||||
|
pipedInputStream.close();
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (Exception e){
|
||||||
System.out.println("'ParseThread' thread exception");
|
System.out.println("'ParseThread' thread exception");
|
||||||
ioe.printStackTrace();
|
e.printStackTrace();
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
System.out.println("ParseThread thread died.");
|
System.out.println("Thread dies");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -267,6 +267,7 @@ public class NCAProvider {
|
||||||
}
|
}
|
||||||
catch (Exception e){
|
catch (Exception e){
|
||||||
e.printStackTrace();
|
e.printStackTrace();
|
||||||
|
System.out.println("No title.keys loaded?");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
11
src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java
Normal file
11
src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
|
public interface IPFS0Provider {
|
||||||
|
String getMagic();
|
||||||
|
int getFilesCount();
|
||||||
|
int getStringTableSize();
|
||||||
|
byte[] getPadding();
|
||||||
|
|
||||||
|
long getRawFileDataStart();
|
||||||
|
PFS0subFile[] getPfs0subFiles();
|
||||||
|
}
|
114
src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java
Normal file
114
src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java
Normal file
|
@ -0,0 +1,114 @@
|
||||||
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
import java.nio.charset.StandardCharsets;
|
||||||
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
import static konogonka.LoperConverter.getLEint;
|
||||||
|
import static konogonka.LoperConverter.getLElong;
|
||||||
|
|
||||||
|
public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
|
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
||||||
|
|
||||||
|
private String magic;
|
||||||
|
private int filesCount;
|
||||||
|
private int stringTableSize;
|
||||||
|
private byte[] padding;
|
||||||
|
private PFS0subFile[] pfs0subFiles;
|
||||||
|
|
||||||
|
//---------------------------------------
|
||||||
|
/*
|
||||||
|
absOffsetPosOfMediaBlock
|
||||||
|
|
||||||
|
Counter - PFS0 Position
|
||||||
|
mediaBlockSize - PFS0 Subsustem Size
|
||||||
|
* */
|
||||||
|
//---------------------------------------
|
||||||
|
|
||||||
|
public PFS0EncryptedProvider(PipedInputStream pipedInputStream) throws Exception{
|
||||||
|
byte[] fileStartingBytes = new byte[0x10];
|
||||||
|
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
||||||
|
|
||||||
|
for (int i = 0; i < 0x10; i++){
|
||||||
|
int currentByte = pipedInputStream.read();
|
||||||
|
if (currentByte == -1) {
|
||||||
|
throw new Exception("PFS0: Reading stream suddenly ended while trying to read starting 0x10 bytes");
|
||||||
|
}
|
||||||
|
fileStartingBytes[i] = (byte)currentByte;
|
||||||
|
}
|
||||||
|
// Check PFS0Provider
|
||||||
|
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
||||||
|
if (! magic.equals("PFS0")){
|
||||||
|
throw new Exception("PFS0Provider: Bad magic");
|
||||||
|
}
|
||||||
|
// Get files count
|
||||||
|
filesCount = getLEint(fileStartingBytes, 0x4);
|
||||||
|
if (filesCount <= 0 ) {
|
||||||
|
throw new Exception("PFS0Provider: Files count is too small");
|
||||||
|
}
|
||||||
|
// Get string table
|
||||||
|
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
||||||
|
if (stringTableSize <= 0 ){
|
||||||
|
throw new Exception("PFS0Provider: String table is too small");
|
||||||
|
}
|
||||||
|
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
pfs0subFiles = new PFS0subFile[filesCount];
|
||||||
|
|
||||||
|
long[] offsetsSubFiles = new long[filesCount];
|
||||||
|
long[] sizesSubFiles = new long[filesCount];
|
||||||
|
int[] strTableOffsets = new int[filesCount];
|
||||||
|
byte[][] zeroBytes = new byte[filesCount][];
|
||||||
|
|
||||||
|
byte[] fileEntryTable = new byte[0x18];
|
||||||
|
for (int i=0; i < filesCount; i++){
|
||||||
|
for (int j = 0; j < 0x18; j++){
|
||||||
|
int currentByte = pipedInputStream.read();
|
||||||
|
if (currentByte == -1) {
|
||||||
|
throw new Exception("PFS0: Reading stream suddenly ended while trying to read File Entry Table #"+i);
|
||||||
|
}
|
||||||
|
fileEntryTable[j] = (byte)currentByte;
|
||||||
|
}
|
||||||
|
offsetsSubFiles[i] = getLElong(fileEntryTable, 0);
|
||||||
|
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
||||||
|
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
||||||
|
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
||||||
|
}
|
||||||
|
//**********************************************************************************************************
|
||||||
|
// In here pointer in front of String table
|
||||||
|
String[] subFileNames = new String[filesCount];
|
||||||
|
byte[] stringTbl = new byte[stringTableSize];
|
||||||
|
|
||||||
|
for (int i = 0; i < stringTableSize; i++){
|
||||||
|
int currentByte = pipedInputStream.read();
|
||||||
|
if (currentByte == -1) {
|
||||||
|
throw new Exception("PFS0: Reading stream suddenly ended while trying to read string table");
|
||||||
|
}
|
||||||
|
stringTbl[i] = (byte)currentByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < filesCount; i++){
|
||||||
|
int j = 0;
|
||||||
|
while (stringTbl[strTableOffsets[i]+j] != (byte)0x00)
|
||||||
|
j++;
|
||||||
|
subFileNames[i] = new String(stringTbl, strTableOffsets[i], j, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < filesCount; i++){
|
||||||
|
pfs0subFiles[i] = new PFS0subFile(
|
||||||
|
subFileNames[i],
|
||||||
|
offsetsSubFiles[i],
|
||||||
|
sizesSubFiles[i],
|
||||||
|
zeroBytes[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rawFileDataStart = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getMagic() { return magic; }
|
||||||
|
public int getFilesCount() { return filesCount; }
|
||||||
|
public int getStringTableSize() { return stringTableSize; }
|
||||||
|
public byte[] getPadding() { return padding; }
|
||||||
|
|
||||||
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
|
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||||
|
}
|
|
@ -3,15 +3,14 @@ package konogonka.Tools.PFS0;
|
||||||
import konogonka.RainbowHexDump;
|
import konogonka.RainbowHexDump;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
|
||||||
import java.io.RandomAccessFile;
|
import java.io.RandomAccessFile;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
import static konogonka.LoperConverter.*;
|
import static konogonka.LoperConverter.*;
|
||||||
|
|
||||||
public class PFS0Provider {
|
public class PFS0Provider implements IPFS0Provider{
|
||||||
private long rawFileDataStart;
|
private long rawFileDataStart; // If -1 then this PFS0 located @ encrypted region
|
||||||
|
|
||||||
private String magic;
|
private String magic;
|
||||||
private int filesCount;
|
private int filesCount;
|
||||||
|
@ -22,80 +21,76 @@ public class PFS0Provider {
|
||||||
public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); }
|
public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); }
|
||||||
|
|
||||||
public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{
|
public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{
|
||||||
try {
|
|
||||||
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r");
|
|
||||||
|
|
||||||
raf.seek(pfs0offsetPosition);
|
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream
|
||||||
byte[] fileStartingBytes = new byte[0x10];
|
|
||||||
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
|
||||||
if (raf.read(fileStartingBytes) != 0x10){
|
|
||||||
raf.close();
|
|
||||||
throw new Exception("PFS0Provider: Unable to read starting bytes");
|
|
||||||
}
|
|
||||||
// Check PFS0Provider
|
|
||||||
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
|
||||||
if (! magic.equals("PFS0")){
|
|
||||||
raf.close();
|
|
||||||
throw new Exception("PFS0Provider: Bad magic");
|
|
||||||
}
|
|
||||||
// Get files count
|
|
||||||
filesCount = getLEint(fileStartingBytes, 0x4);
|
|
||||||
if (filesCount <= 0 ) {
|
|
||||||
raf.close();
|
|
||||||
throw new Exception("PFS0Provider: Files count is too small");
|
|
||||||
}
|
|
||||||
// Get string table
|
|
||||||
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
|
||||||
if (stringTableSize <= 0 ){
|
|
||||||
raf.close();
|
|
||||||
throw new Exception("PFS0Provider: String table is too small");
|
|
||||||
}
|
|
||||||
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
|
||||||
//---------------------------------------------------------------------------------------------------------
|
|
||||||
pfs0subFiles = new PFS0subFile[filesCount];
|
|
||||||
|
|
||||||
long[] offsetsSubFiles = new long[filesCount];
|
raf.seek(pfs0offsetPosition);
|
||||||
long[] sizesSubFiles = new long[filesCount];
|
byte[] fileStartingBytes = new byte[0x10];
|
||||||
int[] strTableOffsets = new int[filesCount];
|
// Read PFS0Provider, files count, header, padding (4 zero bytes)
|
||||||
byte[][] zeroBytes = new byte[filesCount][];
|
if (raf.read(fileStartingBytes) != 0x10){
|
||||||
|
|
||||||
byte[] fileEntryTable = new byte[0x18];
|
|
||||||
for (int i=0; i<filesCount; i++){
|
|
||||||
if (raf.read(fileEntryTable) != 0x18)
|
|
||||||
throw new Exception("PFS0Provider: String table is too small");
|
|
||||||
offsetsSubFiles[i] = getLElong(fileEntryTable, 0);
|
|
||||||
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
|
||||||
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
|
||||||
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
|
||||||
}
|
|
||||||
//**********************************************************************************************************
|
|
||||||
// In here pointer in front of String table
|
|
||||||
String[] subFileNames = new String[filesCount];
|
|
||||||
byte[] stringTbl = new byte[stringTableSize];
|
|
||||||
if (raf.read(stringTbl) != stringTableSize){
|
|
||||||
throw new Exception("Read PFS0Provider String table failure. Can't read requested string table size ("+stringTableSize+")");
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i=0; i < filesCount; i++){
|
|
||||||
int j = 0;
|
|
||||||
while (stringTbl[strTableOffsets[i]+j] != (byte)0x00)
|
|
||||||
j++;
|
|
||||||
subFileNames[i] = new String(stringTbl, strTableOffsets[i], j, StandardCharsets.UTF_8);
|
|
||||||
}
|
|
||||||
for (int i = 0; i < filesCount; i++){
|
|
||||||
pfs0subFiles[i] = new PFS0subFile(
|
|
||||||
subFileNames[i],
|
|
||||||
offsetsSubFiles[i],
|
|
||||||
sizesSubFiles[i],
|
|
||||||
zeroBytes[i]
|
|
||||||
);
|
|
||||||
}
|
|
||||||
rawFileDataStart = raf.getFilePointer();
|
|
||||||
raf.close();
|
raf.close();
|
||||||
|
throw new Exception("PFS0Provider: Unable to read starting bytes");
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
// Check PFS0Provider
|
||||||
throw new IOException("PFS0Provider: "+ioe.getMessage());
|
magic = new String(fileStartingBytes, 0x0, 0x4, StandardCharsets.US_ASCII);
|
||||||
|
if (! magic.equals("PFS0")){
|
||||||
|
raf.close();
|
||||||
|
throw new Exception("PFS0Provider: Bad magic");
|
||||||
}
|
}
|
||||||
|
// Get files count
|
||||||
|
filesCount = getLEint(fileStartingBytes, 0x4);
|
||||||
|
if (filesCount <= 0 ) {
|
||||||
|
raf.close();
|
||||||
|
throw new Exception("PFS0Provider: Files count is too small");
|
||||||
|
}
|
||||||
|
// Get string table
|
||||||
|
stringTableSize = getLEint(fileStartingBytes, 0x8);
|
||||||
|
if (stringTableSize <= 0 ){
|
||||||
|
raf.close();
|
||||||
|
throw new Exception("PFS0Provider: String table is too small");
|
||||||
|
}
|
||||||
|
padding = Arrays.copyOfRange(fileStartingBytes, 0xc, 0x10);
|
||||||
|
//---------------------------------------------------------------------------------------------------------
|
||||||
|
pfs0subFiles = new PFS0subFile[filesCount];
|
||||||
|
|
||||||
|
long[] offsetsSubFiles = new long[filesCount];
|
||||||
|
long[] sizesSubFiles = new long[filesCount];
|
||||||
|
int[] strTableOffsets = new int[filesCount];
|
||||||
|
byte[][] zeroBytes = new byte[filesCount][];
|
||||||
|
|
||||||
|
byte[] fileEntryTable = new byte[0x18];
|
||||||
|
for (int i=0; i<filesCount; i++){
|
||||||
|
if (raf.read(fileEntryTable) != 0x18)
|
||||||
|
throw new Exception("PFS0Provider: String table is too small");
|
||||||
|
offsetsSubFiles[i] = getLElong(fileEntryTable, 0);
|
||||||
|
sizesSubFiles[i] = getLElong(fileEntryTable, 0x8);
|
||||||
|
strTableOffsets[i] = getLEint(fileEntryTable, 0x10);
|
||||||
|
zeroBytes[i] = Arrays.copyOfRange(fileEntryTable, 0x14, 0x18);
|
||||||
|
}
|
||||||
|
//**********************************************************************************************************
|
||||||
|
// In here pointer in front of String table
|
||||||
|
String[] subFileNames = new String[filesCount];
|
||||||
|
byte[] stringTbl = new byte[stringTableSize];
|
||||||
|
if (raf.read(stringTbl) != stringTableSize){
|
||||||
|
throw new Exception("Read PFS0Provider String table failure. Can't read requested string table size ("+stringTableSize+")");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int i=0; i < filesCount; i++){
|
||||||
|
int j = 0;
|
||||||
|
while (stringTbl[strTableOffsets[i]+j] != (byte)0x00)
|
||||||
|
j++;
|
||||||
|
subFileNames[i] = new String(stringTbl, strTableOffsets[i], j, StandardCharsets.UTF_8);
|
||||||
|
}
|
||||||
|
for (int i = 0; i < filesCount; i++){
|
||||||
|
pfs0subFiles[i] = new PFS0subFile(
|
||||||
|
subFileNames[i],
|
||||||
|
offsetsSubFiles[i],
|
||||||
|
sizesSubFiles[i],
|
||||||
|
zeroBytes[i]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
rawFileDataStart = raf.getFilePointer();
|
||||||
|
raf.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getMagic() { return magic; }
|
public String getMagic() { return magic; }
|
||||||
|
|
Loading…
Reference in a new issue