starting re-implementing logic to pipe-streams. Done for PFS0 & HFS0, Updated extractor logic (must be improved later on)

This commit is contained in:
Dmitry Isaenko 2019-08-18 07:16:24 +03:00
parent b1d1ae3d1d
commit 4acbf501d5
15 changed files with 315 additions and 84 deletions

View file

@ -187,7 +187,8 @@ public class NCAController implements TabController {
NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3());
// Section content blocks
// TODO: FIX: This code executes getNCAContentPFS0() method twice
NCAContentPFS0 ncaContentPFS0 = ncaProvider.getNCAContentPFS0(0);
NCAContentPFS0 ncaContentPFS0;
ncaContentPFS0 = ncaProvider.getNCAContentPFS0(0);
NCASectionContentFirstController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());
ncaContentPFS0 = ncaProvider.getNCAContentPFS0(1);
NCASectionContentSecondController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());

View file

@ -6,6 +6,7 @@ import javafx.scene.control.Label;
import konogonka.Controllers.IRowModel;
import konogonka.Controllers.TabController;
import konogonka.MediatorControl;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.PFS0.IPFS0Provider;
import konogonka.Tools.PFS0.PFS0Provider;
import konogonka.Workers.AnalyzerNSP;
@ -47,7 +48,8 @@ public class NSPController implements TabController {
private void extractFiles(){
List<IRowModel> models = tableFilesListController.getFilesForDump();
if (models != null && !models.isEmpty()){
ISuperProvider provider = tableFilesListController.getProvider();
if (models != null && !models.isEmpty() && (provider != null)){
File dir = new File(System.getProperty("user.dir")+File.separator+selectedFile.getName()+" extracted");
try {
@ -61,7 +63,8 @@ public class NSPController implements TabController {
extractBtn.setDisable(true);
NspXciExtractor extractor = new NspXciExtractor(rawFileDataStart, models, dir.getAbsolutePath()+File.separator, selectedFile);
//NspXciExtractor extractor = new NspXciExtractor(rawFileDataStart, models, dir.getAbsolutePath()+File.separator, selectedFile); //TODO: REMOVE
NspXciExtractor extractor = new NspXciExtractor(provider, models, dir.getAbsolutePath()+File.separator);
extractor.setOnSucceeded(e->{
extractBtn.setDisable(false);
});

View file

@ -17,12 +17,12 @@ import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import konogonka.Controllers.IRowModel;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.PFS0.IPFS0Provider;
import konogonka.Tools.PFS0.PFS0Provider;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
@ -31,6 +31,8 @@ public class Pfs0TableViewController implements Initializable {
private TableView<Pfs0RowModel> table;
private ObservableList<Pfs0RowModel> rowsObsLst;
private ISuperProvider provider;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
rowsObsLst = FXCollections.observableArrayList();
@ -146,6 +148,7 @@ public class Pfs0TableViewController implements Initializable {
* Add files when user selected them
* */
public void setNSPToTable(IPFS0Provider pfs){
this.provider = pfs;
rowsObsLst.clear();
Pfs0RowModel.resetNumCnt();
if (pfs == null) {
@ -163,7 +166,7 @@ public class Pfs0TableViewController implements Initializable {
table.refresh();
}
/**
* Return files ready for upload. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined
* Return list of models checked. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined
* @return null if no files marked for upload
* List<File> if there are files
* */
@ -178,4 +181,5 @@ public class Pfs0TableViewController implements Initializable {
}
return models;
}
public ISuperProvider getProvider(){ return provider; }
}

View file

@ -7,6 +7,7 @@ import javafx.scene.control.Label;
import javafx.scene.control.TitledPane;
import konogonka.Controllers.IRowModel;
import konogonka.MediatorControl;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.XCI.HFS0Provider;
import konogonka.Workers.NspXciExtractor;
@ -90,8 +91,9 @@ public class HFSBlockController implements Initializable {
List<IRowModel> models;
models = hfs0tableFilesListMainController.getFilesForDump();
ISuperProvider provider = hfs0tableFilesListMainController.getProvider();
if (models != null && !models.isEmpty()){
if (models != null && !models.isEmpty() && (provider != null)){
File dir = new File(System.getProperty("user.dir")+File.separator+selectedFile.getName()+" "+type+" extracted");
try {
dir.mkdir();
@ -104,7 +106,8 @@ public class HFSBlockController implements Initializable {
extractMainBtn.setDisable(true);
System.out.println(dir.getAbsolutePath()+File.separator);
NspXciExtractor extractor = new NspXciExtractor(bodySize, models, dir.getAbsolutePath()+File.separator, selectedFile);
//NspXciExtractor extractor = new NspXciExtractor(bodySize, models, dir.getAbsolutePath()+File.separator, selectedFile); // TODO: REMOVE
NspXciExtractor extractor = new NspXciExtractor(provider, models, dir.getAbsolutePath()+File.separator);
extractor.setOnSucceeded(e->{
extractMainBtn.setDisable(false);
});

View file

@ -17,12 +17,16 @@ import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.util.Callback;
import konogonka.Controllers.IRowModel;
import konogonka.Tools.ISuperProvider;
import konogonka.Tools.XCI.HFS0Provider;
import java.lang.reflect.Array;
import java.net.URL;
import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.ResourceBundle;
@ -31,6 +35,8 @@ public class Hfs0TableViewController implements Initializable {
private TableView<Hfs0RowModel> table;
private ObservableList<Hfs0RowModel> rowsObsLst;
private ISuperProvider provider;
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
rowsObsLst = FXCollections.observableArrayList();
@ -168,12 +174,14 @@ public class Hfs0TableViewController implements Initializable {
* Add files when user selected them
* */
public void setContentToTable(HFS0Provider hfs0){
this.provider = hfs0;
rowsObsLst.clear();
Hfs0RowModel.resetNumCnt();
if (hfs0 == null) {
table.refresh();
return;
}
// Note: 'i' in here is extra important to be stored in sequence items added.
for (int i = 0; i < hfs0.getFilesCnt(); i++){
rowsObsLst.add(new Hfs0RowModel(
hfs0.getHfs0Files()[i].getName(),
@ -188,7 +196,7 @@ public class Hfs0TableViewController implements Initializable {
table.refresh();
}
/**
* Return files ready for upload. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined
* Return list of models selected. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined
* @return null if no files marked for upload
* List<File> if there are files
* */
@ -203,4 +211,6 @@ public class Hfs0TableViewController implements Initializable {
}
return models;
}
public ISuperProvider getProvider(){ return provider; }
}

View file

@ -0,0 +1,8 @@
package konogonka.Tools;
import java.io.PipedInputStream;
public interface ISuperProvider {
PipedInputStream getProviderSubFilePipedInpStream(String subFileName);
PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber);
}

View file

@ -45,7 +45,7 @@ public class NCAContentPFS0 {
pfs0 = new PFS0Provider(file, pfs0Location);
}
// If encrypted (regular)
else if (ncaSectionBlock.getCryptoType() == 0x3){
else if (ncaSectionBlock.getCryptoType() == 0x03){
new CryptoSection03(file,
offsetPosition,
decryptedKey,
@ -139,8 +139,8 @@ public class NCAContentPFS0 {
);
}
//****************************************___DEBUG___*******************************************************
//*
File contentFile = new File("/tmp/decryptedNCA0block.pfs0");
/*
File contentFile = new File("/tmp/decryptedNCA0block_"+offsetPosition+".pfs0");
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
raf = new RandomAccessFile(file, "r");

View file

@ -252,7 +252,7 @@ public class NCAProvider {
// If empty Rights ID
if (Arrays.equals(rightsId, new byte[0x10])) {
key = decryptedKey2; // TODO: Just remember this dumb hach
key = decryptedKey2; // TODO: Just remember this dumb hack
}
else {
byte[] rightsIDkey = hexStrToByteArray(keys.get(byteArrToHexString(rightsId)));

View file

@ -1,6 +1,8 @@
package konogonka.Tools.PFS0;
public interface IPFS0Provider {
import konogonka.Tools.ISuperProvider;
public interface IPFS0Provider extends ISuperProvider {
boolean isEncrypted();
String getMagic();
int getFilesCount();

View file

@ -22,6 +22,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
private long rawBlockDataStart;
private PFS0DecryptedStreamProvider pfs0DecryptedStreamProvider;
// Let's do some fuck
private class PFS0DecryptedStreamProvider{
private long mediaStartOffset; // * 0x200
@ -38,7 +39,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
public void getStarted(PFS0subFile subFile) throws Exception{
System.out.println("rawBlockDataStart: "+rawBlockDataStart);
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
@ -255,4 +256,16 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
public long getRawFileDataStart() { return rawFileDataStart; }
@Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) {
//TODO
return null;
}
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) {
//TODO
return null;
}
}

View file

@ -1,9 +1,9 @@
package konogonka.Tools.PFS0;
import konogonka.ModelControllers.EMsgType;
import konogonka.RainbowHexDump;
import java.io.File;
import java.io.RandomAccessFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
@ -18,10 +18,12 @@ public class PFS0Provider implements IPFS0Provider{
private byte[] padding;
private PFS0subFile[] pfs0subFiles;
private File file;
public PFS0Provider(File fileWithPfs0) throws Exception{ this(fileWithPfs0, 0); }
public PFS0Provider(File fileWithPfs0, long pfs0offsetPosition) throws Exception{
file = fileWithPfs0;
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream
raf.seek(pfs0offsetPosition);
@ -107,4 +109,73 @@ public class PFS0Provider implements IPFS0Provider{
public long getRawFileDataStart() { return rawFileDataStart; }
@Override
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){ // TODO: Throw exceptions?
if (subFileNumber >= pfs0subFiles.length) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists");
return null;
}
PipedOutputStream streamOut = new PipedOutputStream();
Thread workerThread;
try{
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Executing thread");
try {
long subFileRealPosition = rawFileDataStart + pfs0subFiles[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to skip requested offset");
return;
}
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = pfs0subFiles[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize) {
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to read requested size from file.");
return;
}
streamOut.write(readBuf);
readFrom += readPice;
}
bis.close();
streamOut.close();
} catch (IOException ioe) {
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Thread died");
}
});
workerThread.start();
return streamIn;
}
catch (IOException ioe){
System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream");
return null;
}
}
/**
* Some sugar
* */
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){
for (int i = 0; i < pfs0subFiles.length; i++){
if (pfs0subFiles[i].getName().equals(subFileName))
return getProviderSubFilePipedInpStream(i);
}
return null;
}
}

View file

@ -1,19 +1,18 @@
package konogonka.Tools.XCI;
import konogonka.RainbowHexDump;
import konogonka.Tools.ISuperProvider;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
import java.util.LinkedList;
import static konogonka.LoperConverter.*;
/**
* HFS0
* */
public class HFS0Provider {
public class HFS0Provider implements ISuperProvider {
private boolean magicHFS0;
private int filesCnt;
@ -23,7 +22,10 @@ public class HFS0Provider {
private HFS0File[] hfs0Files;
HFS0Provider(long hfsOffsetPosition, RandomAccessFile raf) throws Exception{
private File file;
HFS0Provider(long hfsOffsetPosition, RandomAccessFile raf, File file) throws Exception{
this.file = file; // Will be used @ getHfs0FilePipedInpStream. It's a bad implementation.
byte[] hfs0bytes = new byte[16];
try{
raf.seek(hfsOffsetPosition);
@ -106,4 +108,72 @@ public class HFS0Provider {
public long getRawFileDataStart() { return rawFileDataStart; }
public HFS0File[] getHfs0Files() { return hfs0Files; }
@Override
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){
PipedOutputStream streamOut = new PipedOutputStream();
Thread workerThread;
if (subFileNumber >= hfs0Files.length) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Requested sub file doesn't exists");
return null;
}
try{
PipedInputStream streamIn = new PipedInputStream(streamOut);
workerThread = new Thread(() -> {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Executing thread");
try{
long subFileRealPosition = rawFileDataStart + hfs0Files[subFileNumber].getOffset();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
if (bis.skip(subFileRealPosition) != subFileRealPosition) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to skip requested offset");
return;
}
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
long readFrom = 0;
long realFileSize = hfs0Files[subFileNumber].getSize();
byte[] readBuf;
while (readFrom < realFileSize){
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
if (bis.read(readBuf) != readPice) {
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to read requested size from file.");
return;
}
streamOut.write(readBuf, 0, readPice);
readFrom += readPice;
}
bis.close();
streamOut.close();
}
catch (IOException ioe){
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
ioe.printStackTrace();
}
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Thread died");
});
workerThread.start();
return streamIn;
}
catch (IOException ioe){
System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Unable to provide stream");
return null;
}
}
/**
* Sugar
* */
@Override
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){
for (int i = 0; i < hfs0Files.length; i++){
if (hfs0Files[i].getName().equals(subFileName))
return getProviderSubFilePipedInpStream(i);
}
return null;
}
}

View file

@ -55,7 +55,7 @@ public class XCIProvider{
}
xciGamecardCert = new XCIGamecardCert(gamecardCertBytes);
hfs0ProviderMain = new HFS0Provider(0xf000, raf);
hfs0ProviderMain = new HFS0Provider(0xf000, raf, file);
if (hfs0ProviderMain.getFilesCnt() < 3){
raf.close();
throw new Exception("XCI Can't read Gamecard certificate bytes.");
@ -65,19 +65,19 @@ public class XCIProvider{
for (HFS0File hfs0File: hfs0ProviderMain.getHfs0Files()){
partition = hfs0File.getName();
if (partition.equals("update")) {
hfs0ProviderUpdate = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
hfs0ProviderUpdate = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
continue;
}
if (partition.equals("normal")) {
hfs0ProviderNormal = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
hfs0ProviderNormal = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
continue;
}
if (partition.equals("secure")) {
hfs0ProviderSecure = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
hfs0ProviderSecure = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
continue;
}
if (partition.equals("logo")) {
hfs0ProviderLogo = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
hfs0ProviderLogo = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
}
}
raf.close();

View file

@ -4,18 +4,64 @@ import javafx.concurrent.Task;
import konogonka.Controllers.IRowModel;
import konogonka.ModelControllers.EMsgType;
import konogonka.ModelControllers.LogPrinter;
import konogonka.Tools.ISuperProvider;
import java.io.*;
import java.util.List;
public class NspXciExtractor extends Task<Void> {
private ISuperProvider provider;
private List<IRowModel> models;
private LogPrinter logPrinter;
private String filesDestPath;
public NspXciExtractor(ISuperProvider provider, List<IRowModel> models, String filesDestPath){
this.provider = provider;
this.models = models;
this.filesDestPath = filesDestPath;
this.logPrinter = new LogPrinter();
for (IRowModel model : models) {
System.out.println(model.getFileName());
}
}
@Override
protected Void call() { // TODO: add cute progress bar
for (IRowModel model : models) {
logPrinter.print("\tStart extracting: "+model.getFileName(), EMsgType.INFO);
File contentFile = new File(filesDestPath + model.getFileName());
try {
BufferedOutputStream extractedFileBOS = new BufferedOutputStream(new FileOutputStream(contentFile));
PipedInputStream pis = provider.getProviderSubFilePipedInpStream(model.getNumber());
byte[] readBuf = new byte[0x800000]; // 8mb NOTE: consider switching to 1mb 1048576
int readSize;
while ((readSize = pis.read(readBuf)) > -1) {
extractedFileBOS.write(readBuf, 0, readSize);
readBuf = new byte[0x800000];
}
extractedFileBOS.close();
} catch (IOException ioe) {
logPrinter.print("\tRead/Write error\n\t" + ioe.getMessage(), EMsgType.INFO);
return null;
} finally {
logPrinter.print("\tEnd extracting", EMsgType.INFO);
logPrinter.close();
}
}
return null;
}
/*
private long rawDataStartPos;
private List<IRowModel> models;
private String filesDestPath;
private LogPrinter logPrinter;
private File NspXciFile;
public NspXciExtractor(long rawDataStartPos, List<IRowModel> models, String filesDestPath, File NspXciFile){
this.rawDataStartPos = rawDataStartPos;
this.models = models;
@ -29,7 +75,6 @@ public class NspXciExtractor extends Task<Void> {
logPrinter.print("\tStart extracting", EMsgType.INFO);
for (IRowModel model: models){
logPrinter.print(filesDestPath+model.getFileName(), EMsgType.INFO);
File contentFile = new File(filesDestPath+model.getFileName());
long realFileOffset = rawDataStartPos + model.getFileOffset();
@ -50,10 +95,10 @@ public class NspXciExtractor extends Task<Void> {
}
while (readFrom < realFileSize){
/*
if (isCancelled()) // Check if user interrupted process.
return false;
*/
// if (isCancelled()) // Check if user interrupted process.
// return false;
if (realFileSize - readFrom < readPice)
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
readBuf = new byte[readPice];
@ -96,4 +141,5 @@ public class NspXciExtractor extends Task<Void> {
logPrinter.print("\tEnd extracting", EMsgType.INFO);
logPrinter.close();
}
*/
}

View file

@ -45,7 +45,7 @@ class XTSTweak {
static byte[] nintTweakFunction(long tweakValue) {
byte[] bs = new byte[BLOCK_SIZE];
byte[] twk = Pack.longToBigEndian(tweakValue);
int j = BLOCK_SIZE-twk.length;
int j = BLOCK_SIZE - twk.length;
for (byte b: twk){
bs[j++] = b;
}