starting re-implementing logic to pipe-streams. Done for PFS0 & HFS0, Updated extractor logic (must be improved later on)
This commit is contained in:
parent
b1d1ae3d1d
commit
4acbf501d5
15 changed files with 315 additions and 84 deletions
|
@ -187,7 +187,8 @@ public class NCAController implements TabController {
|
||||||
NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3());
|
NCASectionHeaderFourthController.populateTab(ncaProvider.getSectionBlock3());
|
||||||
// Section content blocks
|
// Section content blocks
|
||||||
// TODO: FIX: This code executes getNCAContentPFS0() method twice
|
// 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());
|
NCASectionContentFirstController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());
|
||||||
ncaContentPFS0 = ncaProvider.getNCAContentPFS0(1);
|
ncaContentPFS0 = ncaProvider.getNCAContentPFS0(1);
|
||||||
NCASectionContentSecondController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());
|
NCASectionContentSecondController.populateFields(ncaContentPFS0.getPfs0(), selectedFile, ncaContentPFS0.getSHA256hashes());
|
||||||
|
|
|
@ -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.ISuperProvider;
|
||||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
import konogonka.Tools.PFS0.PFS0Provider;
|
||||||
import konogonka.Workers.AnalyzerNSP;
|
import konogonka.Workers.AnalyzerNSP;
|
||||||
|
@ -47,7 +48,8 @@ public class NSPController implements TabController {
|
||||||
|
|
||||||
private void extractFiles(){
|
private void extractFiles(){
|
||||||
List<IRowModel> models = tableFilesListController.getFilesForDump();
|
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");
|
File dir = new File(System.getProperty("user.dir")+File.separator+selectedFile.getName()+" extracted");
|
||||||
try {
|
try {
|
||||||
|
@ -61,7 +63,8 @@ public class NSPController implements TabController {
|
||||||
|
|
||||||
extractBtn.setDisable(true);
|
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->{
|
extractor.setOnSucceeded(e->{
|
||||||
extractBtn.setDisable(false);
|
extractBtn.setDisable(false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,12 +17,12 @@ 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.ISuperProvider;
|
||||||
import konogonka.Tools.PFS0.IPFS0Provider;
|
import konogonka.Tools.PFS0.IPFS0Provider;
|
||||||
import konogonka.Tools.PFS0.PFS0Provider;
|
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
import java.nio.charset.StandardCharsets;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@ -31,6 +31,8 @@ public class Pfs0TableViewController implements Initializable {
|
||||||
private TableView<Pfs0RowModel> table;
|
private TableView<Pfs0RowModel> table;
|
||||||
private ObservableList<Pfs0RowModel> rowsObsLst;
|
private ObservableList<Pfs0RowModel> rowsObsLst;
|
||||||
|
|
||||||
|
private ISuperProvider provider;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
rowsObsLst = FXCollections.observableArrayList();
|
rowsObsLst = FXCollections.observableArrayList();
|
||||||
|
@ -146,6 +148,7 @@ public class Pfs0TableViewController implements Initializable {
|
||||||
* Add files when user selected them
|
* Add files when user selected them
|
||||||
* */
|
* */
|
||||||
public void setNSPToTable(IPFS0Provider pfs){
|
public void setNSPToTable(IPFS0Provider pfs){
|
||||||
|
this.provider = pfs;
|
||||||
rowsObsLst.clear();
|
rowsObsLst.clear();
|
||||||
Pfs0RowModel.resetNumCnt();
|
Pfs0RowModel.resetNumCnt();
|
||||||
if (pfs == null) {
|
if (pfs == null) {
|
||||||
|
@ -163,7 +166,7 @@ public class Pfs0TableViewController implements Initializable {
|
||||||
table.refresh();
|
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
|
* @return null if no files marked for upload
|
||||||
* List<File> if there are files
|
* List<File> if there are files
|
||||||
* */
|
* */
|
||||||
|
@ -178,4 +181,5 @@ public class Pfs0TableViewController implements Initializable {
|
||||||
}
|
}
|
||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
public ISuperProvider getProvider(){ return provider; }
|
||||||
}
|
}
|
|
@ -7,6 +7,7 @@ import javafx.scene.control.Label;
|
||||||
import javafx.scene.control.TitledPane;
|
import javafx.scene.control.TitledPane;
|
||||||
import konogonka.Controllers.IRowModel;
|
import konogonka.Controllers.IRowModel;
|
||||||
import konogonka.MediatorControl;
|
import konogonka.MediatorControl;
|
||||||
|
import konogonka.Tools.ISuperProvider;
|
||||||
import konogonka.Tools.XCI.HFS0Provider;
|
import konogonka.Tools.XCI.HFS0Provider;
|
||||||
import konogonka.Workers.NspXciExtractor;
|
import konogonka.Workers.NspXciExtractor;
|
||||||
|
|
||||||
|
@ -90,8 +91,9 @@ public class HFSBlockController implements Initializable {
|
||||||
List<IRowModel> models;
|
List<IRowModel> models;
|
||||||
|
|
||||||
models = hfs0tableFilesListMainController.getFilesForDump();
|
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");
|
File dir = new File(System.getProperty("user.dir")+File.separator+selectedFile.getName()+" "+type+" extracted");
|
||||||
try {
|
try {
|
||||||
dir.mkdir();
|
dir.mkdir();
|
||||||
|
@ -104,7 +106,8 @@ public class HFSBlockController implements Initializable {
|
||||||
|
|
||||||
extractMainBtn.setDisable(true);
|
extractMainBtn.setDisable(true);
|
||||||
System.out.println(dir.getAbsolutePath()+File.separator);
|
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->{
|
extractor.setOnSucceeded(e->{
|
||||||
extractMainBtn.setDisable(false);
|
extractMainBtn.setDisable(false);
|
||||||
});
|
});
|
||||||
|
|
|
@ -17,12 +17,16 @@ 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.ISuperProvider;
|
||||||
import konogonka.Tools.XCI.HFS0Provider;
|
import konogonka.Tools.XCI.HFS0Provider;
|
||||||
|
|
||||||
|
|
||||||
|
import java.lang.reflect.Array;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.nio.IntBuffer;
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
@ -31,6 +35,8 @@ public class Hfs0TableViewController implements Initializable {
|
||||||
private TableView<Hfs0RowModel> table;
|
private TableView<Hfs0RowModel> table;
|
||||||
private ObservableList<Hfs0RowModel> rowsObsLst;
|
private ObservableList<Hfs0RowModel> rowsObsLst;
|
||||||
|
|
||||||
|
private ISuperProvider provider;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
rowsObsLst = FXCollections.observableArrayList();
|
rowsObsLst = FXCollections.observableArrayList();
|
||||||
|
@ -168,12 +174,14 @@ public class Hfs0TableViewController implements Initializable {
|
||||||
* Add files when user selected them
|
* Add files when user selected them
|
||||||
* */
|
* */
|
||||||
public void setContentToTable(HFS0Provider hfs0){
|
public void setContentToTable(HFS0Provider hfs0){
|
||||||
|
this.provider = hfs0;
|
||||||
rowsObsLst.clear();
|
rowsObsLst.clear();
|
||||||
Hfs0RowModel.resetNumCnt();
|
Hfs0RowModel.resetNumCnt();
|
||||||
if (hfs0 == null) {
|
if (hfs0 == null) {
|
||||||
table.refresh();
|
table.refresh();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Note: 'i' in here is extra important to be stored in sequence items added.
|
||||||
for (int i = 0; i < hfs0.getFilesCnt(); i++){
|
for (int i = 0; i < hfs0.getFilesCnt(); i++){
|
||||||
rowsObsLst.add(new Hfs0RowModel(
|
rowsObsLst.add(new Hfs0RowModel(
|
||||||
hfs0.getHfs0Files()[i].getName(),
|
hfs0.getHfs0Files()[i].getName(),
|
||||||
|
@ -188,7 +196,7 @@ public class Hfs0TableViewController implements Initializable {
|
||||||
table.refresh();
|
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
|
* @return null if no files marked for upload
|
||||||
* List<File> if there are files
|
* List<File> if there are files
|
||||||
* */
|
* */
|
||||||
|
@ -203,4 +211,6 @@ public class Hfs0TableViewController implements Initializable {
|
||||||
}
|
}
|
||||||
return models;
|
return models;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public ISuperProvider getProvider(){ return provider; }
|
||||||
}
|
}
|
8
src/main/java/konogonka/Tools/ISuperProvider.java
Normal file
8
src/main/java/konogonka/Tools/ISuperProvider.java
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
package konogonka.Tools;
|
||||||
|
|
||||||
|
import java.io.PipedInputStream;
|
||||||
|
|
||||||
|
public interface ISuperProvider {
|
||||||
|
PipedInputStream getProviderSubFilePipedInpStream(String subFileName);
|
||||||
|
PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber);
|
||||||
|
}
|
|
@ -45,7 +45,7 @@ public class NCAContentPFS0 {
|
||||||
pfs0 = new PFS0Provider(file, pfs0Location);
|
pfs0 = new PFS0Provider(file, pfs0Location);
|
||||||
}
|
}
|
||||||
// If encrypted (regular)
|
// If encrypted (regular)
|
||||||
else if (ncaSectionBlock.getCryptoType() == 0x3){
|
else if (ncaSectionBlock.getCryptoType() == 0x03){
|
||||||
new CryptoSection03(file,
|
new CryptoSection03(file,
|
||||||
offsetPosition,
|
offsetPosition,
|
||||||
decryptedKey,
|
decryptedKey,
|
||||||
|
@ -139,8 +139,8 @@ public class NCAContentPFS0 {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
//****************************************___DEBUG___*******************************************************
|
//****************************************___DEBUG___*******************************************************
|
||||||
//*
|
/*
|
||||||
File contentFile = new File("/tmp/decryptedNCA0block.pfs0");
|
File contentFile = new File("/tmp/decryptedNCA0block_"+offsetPosition+".pfs0");
|
||||||
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
||||||
|
|
||||||
raf = new RandomAccessFile(file, "r");
|
raf = new RandomAccessFile(file, "r");
|
||||||
|
|
|
@ -252,7 +252,7 @@ public class NCAProvider {
|
||||||
|
|
||||||
// If empty Rights ID
|
// If empty Rights ID
|
||||||
if (Arrays.equals(rightsId, new byte[0x10])) {
|
if (Arrays.equals(rightsId, new byte[0x10])) {
|
||||||
key = decryptedKey2; // TODO: Just remember this dumb hach
|
key = decryptedKey2; // TODO: Just remember this dumb hack
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
byte[] rightsIDkey = hexStrToByteArray(keys.get(byteArrToHexString(rightsId)));
|
byte[] rightsIDkey = hexStrToByteArray(keys.get(byteArrToHexString(rightsId)));
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
package konogonka.Tools.PFS0;
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
public interface IPFS0Provider {
|
import konogonka.Tools.ISuperProvider;
|
||||||
|
|
||||||
|
public interface IPFS0Provider extends ISuperProvider {
|
||||||
boolean isEncrypted();
|
boolean isEncrypted();
|
||||||
String getMagic();
|
String getMagic();
|
||||||
int getFilesCount();
|
int getFilesCount();
|
||||||
|
|
|
@ -22,6 +22,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
private long rawBlockDataStart;
|
private long rawBlockDataStart;
|
||||||
|
|
||||||
private PFS0DecryptedStreamProvider pfs0DecryptedStreamProvider;
|
private PFS0DecryptedStreamProvider pfs0DecryptedStreamProvider;
|
||||||
|
|
||||||
// Let's do some fuck
|
// Let's do some fuck
|
||||||
private class PFS0DecryptedStreamProvider{
|
private class PFS0DecryptedStreamProvider{
|
||||||
private long mediaStartOffset; // * 0x200
|
private long mediaStartOffset; // * 0x200
|
||||||
|
@ -38,7 +39,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{
|
||||||
|
|
||||||
public void getStarted(PFS0subFile subFile) throws Exception{
|
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 blocks: "+rawBlockDataStart/0x200); // aesCtrDecryptSimple.skip(THIS)
|
||||||
System.out.println("Skip bytes: "+ (rawBlockDataStart-(rawBlockDataStart/0x200)*0x200)); // write to stream after skiping 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; }
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
@Override
|
@Override
|
||||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) {
|
||||||
|
//TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) {
|
||||||
|
//TODO
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
package konogonka.Tools.PFS0;
|
package konogonka.Tools.PFS0;
|
||||||
|
|
||||||
|
import konogonka.ModelControllers.EMsgType;
|
||||||
import konogonka.RainbowHexDump;
|
import konogonka.RainbowHexDump;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.*;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
|
@ -18,10 +18,12 @@ public class PFS0Provider implements IPFS0Provider{
|
||||||
private byte[] padding;
|
private byte[] padding;
|
||||||
private PFS0subFile[] pfs0subFiles;
|
private PFS0subFile[] pfs0subFiles;
|
||||||
|
|
||||||
|
private File file;
|
||||||
|
|
||||||
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{
|
||||||
|
file = fileWithPfs0;
|
||||||
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream
|
RandomAccessFile raf = new RandomAccessFile(fileWithPfs0, "r"); // TODO: replace to bufferedInputStream
|
||||||
|
|
||||||
raf.seek(pfs0offsetPosition);
|
raf.seek(pfs0offsetPosition);
|
||||||
|
@ -107,4 +109,73 @@ public class PFS0Provider implements IPFS0Provider{
|
||||||
public long getRawFileDataStart() { return rawFileDataStart; }
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
@Override
|
@Override
|
||||||
public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; }
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,19 +1,18 @@
|
||||||
package konogonka.Tools.XCI;
|
package konogonka.Tools.XCI;
|
||||||
|
|
||||||
import konogonka.RainbowHexDump;
|
import konogonka.RainbowHexDump;
|
||||||
|
import konogonka.Tools.ISuperProvider;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.*;
|
||||||
import java.io.RandomAccessFile;
|
|
||||||
import java.nio.charset.StandardCharsets;
|
import java.nio.charset.StandardCharsets;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedList;
|
|
||||||
|
|
||||||
import static konogonka.LoperConverter.*;
|
import static konogonka.LoperConverter.*;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* HFS0
|
* HFS0
|
||||||
* */
|
* */
|
||||||
public class HFS0Provider {
|
public class HFS0Provider implements ISuperProvider {
|
||||||
|
|
||||||
private boolean magicHFS0;
|
private boolean magicHFS0;
|
||||||
private int filesCnt;
|
private int filesCnt;
|
||||||
|
@ -23,7 +22,10 @@ public class HFS0Provider {
|
||||||
|
|
||||||
private HFS0File[] hfs0Files;
|
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];
|
byte[] hfs0bytes = new byte[16];
|
||||||
try{
|
try{
|
||||||
raf.seek(hfsOffsetPosition);
|
raf.seek(hfsOffsetPosition);
|
||||||
|
@ -106,4 +108,72 @@ public class HFS0Provider {
|
||||||
|
|
||||||
public long getRawFileDataStart() { return rawFileDataStart; }
|
public long getRawFileDataStart() { return rawFileDataStart; }
|
||||||
public HFS0File[] getHfs0Files() { return hfs0Files; }
|
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;
|
||||||
|
}
|
||||||
}
|
}
|
|
@ -55,7 +55,7 @@ public class XCIProvider{
|
||||||
}
|
}
|
||||||
xciGamecardCert = new XCIGamecardCert(gamecardCertBytes);
|
xciGamecardCert = new XCIGamecardCert(gamecardCertBytes);
|
||||||
|
|
||||||
hfs0ProviderMain = new HFS0Provider(0xf000, raf);
|
hfs0ProviderMain = new HFS0Provider(0xf000, raf, file);
|
||||||
if (hfs0ProviderMain.getFilesCnt() < 3){
|
if (hfs0ProviderMain.getFilesCnt() < 3){
|
||||||
raf.close();
|
raf.close();
|
||||||
throw new Exception("XCI Can't read Gamecard certificate bytes.");
|
throw new Exception("XCI Can't read Gamecard certificate bytes.");
|
||||||
|
@ -65,19 +65,19 @@ public class XCIProvider{
|
||||||
for (HFS0File hfs0File: hfs0ProviderMain.getHfs0Files()){
|
for (HFS0File hfs0File: hfs0ProviderMain.getHfs0Files()){
|
||||||
partition = hfs0File.getName();
|
partition = hfs0File.getName();
|
||||||
if (partition.equals("update")) {
|
if (partition.equals("update")) {
|
||||||
hfs0ProviderUpdate = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
|
hfs0ProviderUpdate = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (partition.equals("normal")) {
|
if (partition.equals("normal")) {
|
||||||
hfs0ProviderNormal = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
|
hfs0ProviderNormal = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (partition.equals("secure")) {
|
if (partition.equals("secure")) {
|
||||||
hfs0ProviderSecure = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
|
hfs0ProviderSecure = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (partition.equals("logo")) {
|
if (partition.equals("logo")) {
|
||||||
hfs0ProviderLogo = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf);
|
hfs0ProviderLogo = new HFS0Provider(hfs0ProviderMain.getRawFileDataStart() + hfs0File.getOffset(), raf, file);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
raf.close();
|
raf.close();
|
||||||
|
|
|
@ -4,96 +4,142 @@ import javafx.concurrent.Task;
|
||||||
import konogonka.Controllers.IRowModel;
|
import konogonka.Controllers.IRowModel;
|
||||||
import konogonka.ModelControllers.EMsgType;
|
import konogonka.ModelControllers.EMsgType;
|
||||||
import konogonka.ModelControllers.LogPrinter;
|
import konogonka.ModelControllers.LogPrinter;
|
||||||
|
import konogonka.Tools.ISuperProvider;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class NspXciExtractor extends Task<Void> {
|
public class NspXciExtractor extends Task<Void> {
|
||||||
|
|
||||||
private long rawDataStartPos;
|
private ISuperProvider provider;
|
||||||
private List<IRowModel> models;
|
private List<IRowModel> models;
|
||||||
private String filesDestPath;
|
|
||||||
private LogPrinter logPrinter;
|
private LogPrinter logPrinter;
|
||||||
private File NspXciFile;
|
|
||||||
|
|
||||||
public NspXciExtractor(long rawDataStartPos, List<IRowModel> models, String filesDestPath, File NspXciFile){
|
private String filesDestPath;
|
||||||
this.rawDataStartPos = rawDataStartPos;
|
|
||||||
|
public NspXciExtractor(ISuperProvider provider, List<IRowModel> models, String filesDestPath){
|
||||||
|
this.provider = provider;
|
||||||
this.models = models;
|
this.models = models;
|
||||||
this.filesDestPath = filesDestPath;
|
this.filesDestPath = filesDestPath;
|
||||||
this.NspXciFile = NspXciFile;
|
|
||||||
this.logPrinter = new LogPrinter();
|
this.logPrinter = new LogPrinter();
|
||||||
|
for (IRowModel model : models) {
|
||||||
|
System.out.println(model.getFileName());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void call() {
|
protected Void call() { // TODO: add cute progress bar
|
||||||
logPrinter.print("\tStart extracting", EMsgType.INFO);
|
for (IRowModel model : models) {
|
||||||
for (IRowModel model: models){
|
logPrinter.print("\tStart extracting: "+model.getFileName(), EMsgType.INFO);
|
||||||
logPrinter.print(filesDestPath+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());
|
||||||
|
|
||||||
File contentFile = new File(filesDestPath+model.getFileName());
|
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;
|
||||||
|
|
||||||
long realFileOffset = rawDataStartPos + model.getFileOffset();
|
|
||||||
long realFileSize = model.getFileSize();
|
|
||||||
|
|
||||||
long readFrom = 0;
|
|
||||||
|
|
||||||
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
|
public NspXciExtractor(long rawDataStartPos, List<IRowModel> models, String filesDestPath, File NspXciFile){
|
||||||
byte[] readBuf;
|
this.rawDataStartPos = rawDataStartPos;
|
||||||
|
this.models = models;
|
||||||
|
this.filesDestPath = filesDestPath;
|
||||||
|
this.NspXciFile = NspXciFile;
|
||||||
|
this.logPrinter = new LogPrinter();
|
||||||
|
}
|
||||||
|
|
||||||
try{
|
@Override
|
||||||
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
protected Void call() {
|
||||||
|
logPrinter.print("\tStart extracting", EMsgType.INFO);
|
||||||
|
for (IRowModel model: models){
|
||||||
|
logPrinter.print(filesDestPath+model.getFileName(), EMsgType.INFO);
|
||||||
|
File contentFile = new File(filesDestPath+model.getFileName());
|
||||||
|
|
||||||
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(NspXciFile)); // TODO: refactor?
|
long realFileOffset = rawDataStartPos + model.getFileOffset();
|
||||||
if (bufferedInStream.skip(realFileOffset) != realFileOffset) {
|
long realFileSize = model.getFileSize();
|
||||||
logPrinter.print("File length is less than offset noted", EMsgType.FAIL);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
while (readFrom < realFileSize){
|
long readFrom = 0;
|
||||||
/*
|
|
||||||
if (isCancelled()) // Check if user interrupted process.
|
int readPice = 8388608; // 8mb NOTE: consider switching to 1mb 1048576
|
||||||
return false;
|
byte[] readBuf;
|
||||||
*/
|
|
||||||
if (realFileSize - readFrom < readPice)
|
try{
|
||||||
readPice = Math.toIntExact(realFileSize - readFrom); // it's safe, I guarantee
|
BufferedOutputStream extractedFileOS = new BufferedOutputStream(new FileOutputStream(contentFile));
|
||||||
readBuf = new byte[readPice];
|
|
||||||
if (bufferedInStream.read(readBuf) != readPice) {
|
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(NspXciFile)); // TODO: refactor?
|
||||||
logPrinter.print("Can't read required chunk from file", EMsgType.FAIL);
|
if (bufferedInStream.skip(realFileOffset) != realFileOffset) {
|
||||||
|
logPrinter.print("File length is less than offset noted", EMsgType.FAIL);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
extractedFileOS.write(readBuf, 0, readPice);
|
while (readFrom < realFileSize){
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
if (bufferedInStream.read(readBuf) != readPice) {
|
||||||
|
logPrinter.print("Can't read required chunk from file", EMsgType.FAIL);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
extractedFileOS.write(readBuf, 0, readPice);
|
||||||
|
//-----------------------------------------/
|
||||||
|
try {
|
||||||
|
logPrinter.updateProgress((readFrom+readPice)/(realFileSize/100.0) / 100.0);
|
||||||
|
}catch (InterruptedException ie){
|
||||||
|
getException().printStackTrace(); // TODO: Do something with this
|
||||||
|
}
|
||||||
|
//-----------------------------------------/
|
||||||
|
readFrom += readPice;
|
||||||
|
}
|
||||||
|
bufferedInStream.close();
|
||||||
|
extractedFileOS.close();
|
||||||
//-----------------------------------------/
|
//-----------------------------------------/
|
||||||
try {
|
try{
|
||||||
logPrinter.updateProgress((readFrom+readPice)/(realFileSize/100.0) / 100.0);
|
logPrinter.updateProgress(1.0);
|
||||||
}catch (InterruptedException ie){
|
}
|
||||||
|
catch (InterruptedException ie){
|
||||||
getException().printStackTrace(); // TODO: Do something with this
|
getException().printStackTrace(); // TODO: Do something with this
|
||||||
}
|
}
|
||||||
//-----------------------------------------/
|
//-----------------------------------------/
|
||||||
readFrom += readPice;
|
|
||||||
}
|
}
|
||||||
bufferedInStream.close();
|
catch (IOException ioe){
|
||||||
extractedFileOS.close();
|
logPrinter.print("\tRead/Write error\n\t"+ioe.getMessage(), EMsgType.INFO);
|
||||||
//-----------------------------------------/
|
return null;
|
||||||
try{
|
|
||||||
logPrinter.updateProgress(1.0);
|
|
||||||
}
|
}
|
||||||
catch (InterruptedException ie){
|
}
|
||||||
getException().printStackTrace(); // TODO: Do something with this
|
close();
|
||||||
}
|
return null;
|
||||||
//-----------------------------------------/
|
|
||||||
}
|
|
||||||
catch (IOException ioe){
|
|
||||||
logPrinter.print("\tRead/Write error\n\t"+ioe.getMessage(), EMsgType.INFO);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
close();
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void close(){
|
private void close(){
|
||||||
logPrinter.print("\tEnd extracting", EMsgType.INFO);
|
logPrinter.print("\tEnd extracting", EMsgType.INFO);
|
||||||
logPrinter.close();
|
logPrinter.close();
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
}
|
}
|
|
@ -45,7 +45,7 @@ class XTSTweak {
|
||||||
static byte[] nintTweakFunction(long tweakValue) {
|
static byte[] nintTweakFunction(long tweakValue) {
|
||||||
byte[] bs = new byte[BLOCK_SIZE];
|
byte[] bs = new byte[BLOCK_SIZE];
|
||||||
byte[] twk = Pack.longToBigEndian(tweakValue);
|
byte[] twk = Pack.longToBigEndian(tweakValue);
|
||||||
int j = BLOCK_SIZE-twk.length;
|
int j = BLOCK_SIZE - twk.length;
|
||||||
for (byte b: twk){
|
for (byte b: twk){
|
||||||
bs[j++] = b;
|
bs[j++] = b;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue