From 4e1bacd4fcf2093c5b6d1e6782cdc1e98d74b8e5 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Mon, 19 Aug 2019 17:22:42 +0300 Subject: [PATCH] NCA table/contoller/provider updates --- README.md | 5 +- .../java/konogonka/Child/ChildWindow.java | 70 +++++ .../konogonka/Controllers/ITabController.java | 12 + .../konogonka/Controllers/MainController.java | 10 + .../Controllers/NCA/NCAController.java | 24 +- .../NCA/NCASectionContentController.java | 1 - .../Controllers/NSP/NSPController.java | 9 +- .../NSP/Pfs0TableViewController.java | 24 +- .../konogonka/Controllers/TabController.java | 10 - .../Controllers/XCI/XCIController.java | 11 +- .../java/konogonka/Tools/ISuperProvider.java | 8 +- .../konogonka/Tools/NCA/NCAContentPFS0.java | 26 +- .../java/konogonka/Tools/NCA/NCAProvider.java | 8 +- .../konogonka/Tools/PFS0/IPFS0Provider.java | 1 - .../Tools/PFS0/PFS0EncryptedProvider.java | 244 +++++++++--------- .../konogonka/Tools/PFS0/PFS0Provider.java | 93 +++---- .../konogonka/Tools/XCI/HFS0Provider.java | 92 +++---- .../java/konogonka/Workers/AnalyzerNCA.java | 10 +- .../konogonka/Workers/NspXciExtractor.java | 7 +- src/main/resources/FXML/NCA/NCATab.fxml | 87 +++++-- 20 files changed, 450 insertions(+), 302 deletions(-) create mode 100644 src/main/java/konogonka/Child/ChildWindow.java create mode 100644 src/main/java/konogonka/Controllers/ITabController.java delete mode 100644 src/main/java/konogonka/Controllers/TabController.java diff --git a/README.md b/README.md index 8a28417..6b813dc 100644 --- a/README.md +++ b/README.md @@ -14,8 +14,9 @@ Deep WIP multi-tool to work with XCI/NSP/NCA/NRO(?) files ### Information taken from * Switch brew wiki -* roothorick, shchmue, He and others advices and notes. Thanks! +* Original ScriesM software +* Thanks to roothorick, shchmue, He and others for their advices, notes and examples ### System requirements -JRE/JDK 8u60 or higher. +JRE/JDK 8u60 or higher. \ No newline at end of file diff --git a/src/main/java/konogonka/Child/ChildWindow.java b/src/main/java/konogonka/Child/ChildWindow.java new file mode 100644 index 0000000..afab958 --- /dev/null +++ b/src/main/java/konogonka/Child/ChildWindow.java @@ -0,0 +1,70 @@ +package konogonka.Child; + +import javafx.fxml.FXMLLoader; +import javafx.scene.Parent; +import javafx.scene.Scene; +import javafx.scene.image.Image; +import javafx.stage.Stage; +import konogonka.Controllers.IRowModel; +import konogonka.Controllers.NCA.NCAController; +import konogonka.Tools.ISuperProvider; + +import java.io.IOException; +import java.util.Locale; +import java.util.ResourceBundle; + +public class ChildWindow { + public ChildWindow(ISuperProvider provider, IRowModel model) throws IOException{ + Stage stageSettings = new Stage(); + + stageSettings.setMinWidth(570); + stageSettings.setMinHeight(500); + + FXMLLoader loaderSettings; + + if (model.getFileName().endsWith(".nca")){ + loaderSettings = new FXMLLoader(getClass().getResource("/FXML/NCA/NCATab.fxml")); + } + else if(model.getFileName().endsWith(".cert")){ + // TODO: IMPLEMENT + return; + } + else if(model.getFileName().endsWith(".tik")){ + // TODO: IMPLEMENT + return; + } + else if(model.getFileName().endsWith(".xml")){ + // TODO: IMPLEMENT + return; + } + else + return; + + Locale userLocale = new Locale(Locale.getDefault().getISO3Language()); + ResourceBundle resourceBundle = ResourceBundle.getBundle("locale", userLocale); + loaderSettings.setResources(resourceBundle); + Parent parentAbout = loaderSettings.load(); + + + // TODO: REFACTOR + if (model.getFileName().endsWith(".nca")){ + NCAController ncaController = loaderSettings.getController(); + ncaController.analyze(provider.getFile(), provider.getRawFileDataStart()+model.getFileOffset()); + } + + + stageSettings.setTitle(model.getFileName()); + stageSettings.getIcons().addAll( + new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")), + new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")), + new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")), + new Image(getClass().getResourceAsStream("/res/app_icon128x128.png")) + ); + Scene settingsScene = new Scene(parentAbout, 800, 800); + settingsScene.getStylesheets().add("/res/app_light.css"); + stageSettings.setScene(settingsScene); + stageSettings.setMinWidth(550.0); + stageSettings.setMinHeight(550.0); + stageSettings.show(); + } +} diff --git a/src/main/java/konogonka/Controllers/ITabController.java b/src/main/java/konogonka/Controllers/ITabController.java new file mode 100644 index 0000000..0717d75 --- /dev/null +++ b/src/main/java/konogonka/Controllers/ITabController.java @@ -0,0 +1,12 @@ +package konogonka.Controllers; + +import javafx.fxml.Initializable; +import konogonka.Tools.ISuperProvider; + +import java.io.File; + +public interface ITabController extends Initializable { + void analyze(File file); + void analyze(ISuperProvider provider, int subFileNumber); + void resetTab(); +} diff --git a/src/main/java/konogonka/Controllers/MainController.java b/src/main/java/konogonka/Controllers/MainController.java index 93b9d01..198e53e 100644 --- a/src/main/java/konogonka/Controllers/MainController.java +++ b/src/main/java/konogonka/Controllers/MainController.java @@ -6,11 +6,13 @@ import javafx.scene.control.*; import javafx.scene.layout.AnchorPane; import javafx.stage.FileChooser; import konogonka.AppPreferences; +import konogonka.Child.ChildWindow; import konogonka.Controllers.NCA.NCAController; import konogonka.Controllers.NSP.NSPController; import konogonka.Controllers.XCI.XCIController; import konogonka.MediatorControl; import konogonka.Settings.SettingsWindow; +import konogonka.Tools.ISuperProvider; import java.io.*; import java.net.URL; @@ -115,6 +117,14 @@ public class MainController implements Initializable { else splitPane.getItems().add(logPane); } + public void showContentWindow(ISuperProvider provider, IRowModel model){ + try{ + new ChildWindow(provider, model); + } + catch (IOException e){ + logArea.appendText("\nUnable to create windows for "+model.getFileName()+"\n"+e.getMessage()); + } + }; public void exit(){ AppPreferences.getInstance().setRecentPath(previouslyOpenedPath); } } \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/NCA/NCAController.java b/src/main/java/konogonka/Controllers/NCA/NCAController.java index 1842fb2..fef36b7 100644 --- a/src/main/java/konogonka/Controllers/NCA/NCAController.java +++ b/src/main/java/konogonka/Controllers/NCA/NCAController.java @@ -4,7 +4,8 @@ import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; import konogonka.AppPreferences; -import konogonka.Controllers.TabController; +import konogonka.Controllers.ITabController; +import konogonka.Tools.ISuperProvider; import konogonka.Tools.NCA.NCAContentPFS0; import konogonka.Tools.NCA.NCAProvider; import konogonka.Workers.AnalyzerNCA; @@ -16,7 +17,7 @@ import java.util.ResourceBundle; import static konogonka.LoperConverter.byteArrToHexString; -public class NCAController implements TabController { +public class NCAController implements ITabController { private File selectedFile; @FXML @@ -46,6 +47,7 @@ public class NCAController implements TabController { keyIndexLbl, ncaSizeLbl, titleIdLbl, + contentIndexLbl, sdkVersionLbl, cryptoType2Lbl, ticketLbl; @@ -70,9 +72,12 @@ public class NCAController implements TabController { public void initialize(URL url, ResourceBundle resourceBundle) { } - @Override - public void analyze(File file) { + public void analyze(ISuperProvider provider, int subFileNumber){ + // TODO + } + + public void analyze(File file, long offset) { this.selectedFile = file; HashMap keysMap = new HashMap<>(); keysMap.put("header_key", AppPreferences.getInstance().getHeaderKey()); @@ -90,8 +95,8 @@ public class NCAController implements TabController { if ( ! pair[0].equals("0") && ! pair[1].equals("0")) keysMap.put(pair[0], pair[1]); } - - AnalyzerNCA analyzerNCA = new AnalyzerNCA(file, keysMap); + + AnalyzerNCA analyzerNCA = new AnalyzerNCA(file, keysMap, offset); analyzerNCA.setOnSucceeded(e->{ populateFields(analyzerNCA.getValue()); }); @@ -100,6 +105,11 @@ public class NCAController implements TabController { workThread.start(); } + @Override + public void analyze(File file) { + analyze(file, 0); + } + @Override public void resetTab() { // Header @@ -115,6 +125,7 @@ public class NCAController implements TabController { sdkVersionLbl.setText("-"); cryptoType2Lbl.setText("-"); ticketLbl.setText("-"); + contentIndexLbl.setText("-"); sha256section1TF.setText("-"); sha256section2TF.setText("-"); sha256section3TF.setText("-"); @@ -156,6 +167,7 @@ public class NCAController implements TabController { keyIndexLbl.setText(Byte.toString(ncaProvider.getKeyIndex())); ncaSizeLbl.setText(Long.toString(ncaProvider.getNcaSize())); titleIdLbl.setText(byteArrToHexString(ncaProvider.getTitleId())); + contentIndexLbl.setText(byteArrToHexString(ncaProvider.getContentIndx())); // sdkVersionLbl.setText(ncaProvider.getSdkVersion()[3] +"."+ncaProvider.getSdkVersion()[2] +"."+ncaProvider.getSdkVersion()[1] diff --git a/src/main/java/konogonka/Controllers/NCA/NCASectionContentController.java b/src/main/java/konogonka/Controllers/NCA/NCASectionContentController.java index 226a8a5..fe59ba4 100644 --- a/src/main/java/konogonka/Controllers/NCA/NCASectionContentController.java +++ b/src/main/java/konogonka/Controllers/NCA/NCASectionContentController.java @@ -8,7 +8,6 @@ import javafx.scene.layout.VBox; import konogonka.Controllers.NSP.NSPController; import konogonka.LoperConverter; import konogonka.Tools.PFS0.IPFS0Provider; -import konogonka.Tools.PFS0.PFS0Provider; import java.io.File; import java.util.LinkedList; diff --git a/src/main/java/konogonka/Controllers/NSP/NSPController.java b/src/main/java/konogonka/Controllers/NSP/NSPController.java index 7e96dda..c6d417d 100644 --- a/src/main/java/konogonka/Controllers/NSP/NSPController.java +++ b/src/main/java/konogonka/Controllers/NSP/NSPController.java @@ -4,7 +4,7 @@ import javafx.fxml.FXML; import javafx.scene.control.Button; import javafx.scene.control.Label; import konogonka.Controllers.IRowModel; -import konogonka.Controllers.TabController; +import konogonka.Controllers.ITabController; import konogonka.MediatorControl; import konogonka.Tools.ISuperProvider; import konogonka.Tools.PFS0.IPFS0Provider; @@ -19,7 +19,7 @@ import java.util.ResourceBundle; import static konogonka.LoperConverter.byteArrToHexString; -public class NSPController implements TabController { +public class NSPController implements ITabController { @FXML private Button extractBtn; @@ -92,6 +92,11 @@ public class NSPController implements TabController { * Start analyze NSP * */ @Override + public void analyze(ISuperProvider provider, int subFileNumber){ + // TODO: IMPLEMENT + return; + } + @Override public void analyze(File selectedFile){ this.selectedFile = selectedFile; AnalyzerNSP analyzerNSP = new AnalyzerNSP(selectedFile); diff --git a/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java b/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java index ea2c196..768955c 100644 --- a/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java +++ b/src/main/java/konogonka/Controllers/NSP/Pfs0TableViewController.java @@ -1,10 +1,12 @@ package konogonka.Controllers.NSP; +import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; @@ -17,6 +19,7 @@ import javafx.scene.input.MouseButton; import javafx.scene.input.MouseEvent; import javafx.util.Callback; import konogonka.Controllers.IRowModel; +import konogonka.MediatorControl; import konogonka.Tools.ISuperProvider; import konogonka.Tools.PFS0.IPFS0Provider; @@ -117,15 +120,30 @@ public class Pfs0TableViewController implements Initializable { uploadColumn.setCellFactory(new Callback, TableCell>() { @Override public TableCell call(TableColumn paramFeatures) { - CheckBoxTableCell cell = new CheckBoxTableCell<>(); - return cell; + return new CheckBoxTableCell<>(); } }); table.setRowFactory( // this shit is made to implement context menu. It's such a pain.. new Callback, TableRow>() { @Override - public TableRow call(TableView nslRowModelTableView) { + public TableRow call(TableView Pfs0RowModelTableView) { final TableRow row = new TableRow<>(); + ContextMenu contextMenu = new ContextMenu(); + + MenuItem openMenuItem = new MenuItem("Open"); + openMenuItem.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent actionEvent) { + MediatorControl.getInstance().getContoller().showContentWindow(provider, row.getItem()); // TODO: change to something better + } + }); + + contextMenu.getItems().addAll(openMenuItem); + + row.setContextMenu(contextMenu); + row.contextMenuProperty().bind( + Bindings.when(Bindings.isNotNull(row.itemProperty())).then(contextMenu).otherwise((ContextMenu)null) + ); row.setOnMouseClicked(new EventHandler() { // Just.. don't ask.. @Override public void handle(MouseEvent mouseEvent) { diff --git a/src/main/java/konogonka/Controllers/TabController.java b/src/main/java/konogonka/Controllers/TabController.java deleted file mode 100644 index b131c28..0000000 --- a/src/main/java/konogonka/Controllers/TabController.java +++ /dev/null @@ -1,10 +0,0 @@ -package konogonka.Controllers; - -import javafx.fxml.Initializable; - -import java.io.File; - -public interface TabController extends Initializable { - void analyze(File file); - void resetTab(); -} diff --git a/src/main/java/konogonka/Controllers/XCI/XCIController.java b/src/main/java/konogonka/Controllers/XCI/XCIController.java index 86b58f5..9048669 100644 --- a/src/main/java/konogonka/Controllers/XCI/XCIController.java +++ b/src/main/java/konogonka/Controllers/XCI/XCIController.java @@ -1,11 +1,11 @@ package konogonka.Controllers.XCI; import javafx.fxml.FXML; -import javafx.scene.control.Button; import javafx.scene.control.Label; import javafx.scene.control.TextField; import konogonka.AppPreferences; -import konogonka.Controllers.TabController; +import konogonka.Controllers.ITabController; +import konogonka.Tools.ISuperProvider; import konogonka.Tools.XCI.XCIProvider; import konogonka.Workers.AnalyzerXCI; @@ -15,7 +15,7 @@ import java.util.ResourceBundle; import static konogonka.LoperConverter.byteArrToHexString; -public class XCIController implements TabController { +public class XCIController implements ITabController { /* Header */ @FXML @@ -90,6 +90,11 @@ public class XCIController implements TabController { * Start analyze XCI * */ @Override + public void analyze(ISuperProvider provider, int subFileNumber){ + // TODO: IMPLEMENT + return; + } + @Override public void analyze(File selectedFile){ HFSBlockController.setSelectedFile(selectedFile); diff --git a/src/main/java/konogonka/Tools/ISuperProvider.java b/src/main/java/konogonka/Tools/ISuperProvider.java index 0ec1c74..730bc30 100644 --- a/src/main/java/konogonka/Tools/ISuperProvider.java +++ b/src/main/java/konogonka/Tools/ISuperProvider.java @@ -1,8 +1,12 @@ package konogonka.Tools; +import java.io.File; import java.io.PipedInputStream; public interface ISuperProvider { - PipedInputStream getProviderSubFilePipedInpStream(String subFileName); - PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber); + PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception; + PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception; + + File getFile(); + long getRawFileDataStart(); } diff --git a/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java b/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java index 64e8102..ce9b007 100644 --- a/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java +++ b/src/main/java/konogonka/Tools/NCA/NCAContentPFS0.java @@ -71,12 +71,12 @@ public class NCAContentPFS0 { 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 mediaStartBlocksOffset, long mediaEndBlocksOffset) throws Exception{ /*//-------------------------------------------------------------------------------------------------- - System.out.println("Media start location: " + mediaStartOffset); - System.out.println("Media end location: " + mediaEndOffset); - System.out.println("Media size : " + (mediaEndOffset-mediaStartOffset)); - System.out.println("Media act. location: " + (offsetPosition + (mediaStartOffset * 0x200))); + System.out.println("Media start location: " + mediaStartBlocksOffset); + System.out.println("Media end location: " + mediaEndBlocksOffset); + System.out.println("Media size : " + (mediaEndBlocksOffset-mediaStartBlocksOffset)); + System.out.println("Media act. location: " + (offsetPosition + (mediaStartBlocksOffset * 0x200))); System.out.println("SHA256 hash tbl size: " + ncaSectionBlock.getSuperBlockPFS0().getHashTableSize()); System.out.println("SHA256 hash tbl offs: " + ncaSectionBlock.getSuperBlockPFS0().getHashTableOffset()); System.out.println("PFS0 Offs: " + ncaSectionBlock.getSuperBlockPFS0().getPfs0offset()); @@ -89,14 +89,14 @@ public class NCAContentPFS0 { throw new Exception("CryptoSection03: unable to proceed. No decrypted key provided."); RandomAccessFile raf = new RandomAccessFile(file, "r"); - long abosluteOffsetPosition = offsetPosition + (mediaStartOffset * 0x200); + long abosluteOffsetPosition = offsetPosition + (mediaStartBlocksOffset * 0x200); raf.seek(abosluteOffsetPosition); - AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200); + AesCtrDecryptSimple decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartBlocksOffset * 0x200); byte[] encryptedBlock; byte[] dectyptedBlock; - long mediaBlockSize = mediaEndOffset - mediaStartOffset; + long mediaBlocksSize = mediaEndBlocksOffset - mediaStartBlocksOffset; // Prepare thread to parse encrypted data PipedOutputStream streamOut = new PipedOutputStream(); PipedInputStream streamInp = new PipedInputStream(streamOut); @@ -110,12 +110,12 @@ public class NCAContentPFS0 { file, decryptedKey, ncaSectionBlock.getSectionCTR(), - mediaStartOffset, - mediaEndOffset + mediaStartBlocksOffset, + mediaEndBlocksOffset )); pThread.start(); // Decrypt data - for (int i = 0; i < mediaBlockSize; i++){ + for (int i = 0; i < mediaBlocksSize; i++){ encryptedBlock = new byte[0x200]; if (raf.read(encryptedBlock) != -1){ //dectyptedBlock = aesCtr.decrypt(encryptedBlock); @@ -139,9 +139,9 @@ public class NCAContentPFS0 { raf = new RandomAccessFile(file, "r"); raf.seek(abosluteOffsetPosition); - decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartOffset * 0x200); + decryptor = new AesCtrDecryptSimple(decryptedKey, ncaSectionBlock.getSectionCTR(), mediaStartBlocksOffset * 0x200); - for (int i = 0; i < mediaBlockSize; i++){ + for (int i = 0; i < mediaBlocksSize; i++){ encryptedBlock = new byte[0x200]; if (raf.read(encryptedBlock) != -1){ //dectyptedBlock = aesCtr.decrypt(encryptedBlock); diff --git a/src/main/java/konogonka/Tools/NCA/NCAProvider.java b/src/main/java/konogonka/Tools/NCA/NCAProvider.java index d494ace..2a22f0e 100644 --- a/src/main/java/konogonka/Tools/NCA/NCAProvider.java +++ b/src/main/java/konogonka/Tools/NCA/NCAProvider.java @@ -1,6 +1,5 @@ package konogonka.Tools.NCA; -import konogonka.LoperConverter; import konogonka.Tools.NCA.NCASectionTableBlock.NCASectionBlock; import konogonka.xtsaes.XTSAESCipher; import org.bouncycastle.crypto.params.KeyParameter; @@ -30,6 +29,7 @@ public class NCAProvider { private byte keyIndex; // application/ocean/system (kaek index?) private long ncaSize; // Size of this NCA (bytes) private byte[] titleId; + private byte[] contentIndx; private byte[] sdkVersion; // version ver_revision.ver_micro.vev_minor.ver_major private byte cryptoType2; // keyblob index. Considering as number within application/ocean/system private byte[] rightsId; @@ -139,7 +139,8 @@ public class NCAProvider { cryptoType1 = decryptedData[0x206]; keyIndex = decryptedData[0x207]; ncaSize = getLElong(decryptedData, 0x208); - titleId = Arrays.copyOfRange(decryptedData, 0x210, 0x21C); // 0x218 ? + titleId = Arrays.copyOfRange(decryptedData, 0x210, 0x218); + contentIndx = Arrays.copyOfRange(decryptedData, 0x218, 0x21C); sdkVersion = Arrays.copyOfRange(decryptedData, 0x21c, 0x220); cryptoType2 = decryptedData[0x220]; rightsId = Arrays.copyOfRange(decryptedData, 0x230, 0x240); @@ -214,6 +215,7 @@ public class NCAProvider { public byte getKeyIndex() { return keyIndex; } public long getNcaSize() { return ncaSize; } public byte[] getTitleId() { return titleId; } + public byte[] getContentIndx() { return contentIndx; } public byte[] getSdkVersion() { return sdkVersion; } public byte getCryptoType2() { return cryptoType2; } public byte[] getRightsId() { return rightsId; } @@ -273,7 +275,7 @@ public class NCAProvider { } switch (sectionNumber) { case 0: - return new NCAContentPFS0(file, offset, sectionBlock0, tableEntry0, key); // TODO: remove decryptedKey2 + return new NCAContentPFS0(file, offset, sectionBlock0, tableEntry0, key); // TODO: remove decryptedKey2 ? case 1: return new NCAContentPFS0(file, offset, sectionBlock1, tableEntry1, key); case 2: diff --git a/src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java b/src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java index ee7a261..3934446 100644 --- a/src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java +++ b/src/main/java/konogonka/Tools/PFS0/IPFS0Provider.java @@ -9,6 +9,5 @@ public interface IPFS0Provider extends ISuperProvider { int getStringTableSize(); byte[] getPadding(); - long getRawFileDataStart(); PFS0subFile[] getPfs0subFiles(); } diff --git a/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java b/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java index 2634bc1..0f3e20f 100644 --- a/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java +++ b/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java @@ -25,8 +25,8 @@ public class PFS0EncryptedProvider implements IPFS0Provider{ private File file; private byte[] key; private byte[] sectionCTR; - private long mediaStartOffset; - private long mediaEndOffset; + private long mediaStartOffset; // In 512-blocks + private long mediaEndOffset; // In 512-blocks public PFS0EncryptedProvider(PipedInputStream pipedInputStream, long pfs0offsetPosition, long offsetPositionInFile, @@ -44,7 +44,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{ 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 - rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider + rawFileDataStart = -1; // Set -1 for PFS0EncryptedProvider // Detect raw data start position using next var rawBlockDataStart = pfs0offsetPosition; @@ -145,145 +145,137 @@ public class PFS0EncryptedProvider implements IPFS0Provider{ public long getRawFileDataStart() { return rawFileDataStart; } @Override public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } - @Override - public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) { + public File getFile(){ return file; } + @Override + public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception { if (subFileNumber >= pfs0subFiles.length) { - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); - return null; + throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); } - /*/------------------------------------------------------------------ - 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 + + 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; } - - 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(); + + //----------------------------- 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++; } - System.out.println("PFS0EncryptedProvider -> getPfs0subFilePipedInpStream(): Thread died"); + + 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; - } + }); + workerThread.start(); + return streamIn; } + @Override - public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) { + public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception{ for (int i = 0; i < pfs0subFiles.length; i++){ if (pfs0subFiles[i].getName().equals(subFileName)) return getProviderSubFilePipedInpStream(i); diff --git a/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java b/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java index bb200fc..e025ec3 100644 --- a/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java +++ b/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java @@ -110,68 +110,61 @@ public class PFS0Provider implements IPFS0Provider{ @Override public PFS0subFile[] getPfs0subFiles() { return pfs0subFiles; } @Override - public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){ // TODO: Throw exceptions? + public File getFile(){ return file; } + @Override + public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception{ // TODO: Throw exceptions? if (subFileNumber >= pfs0subFiles.length) { - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); - return null; + throw new Exception("PFS0Provider -> getPfs0subFilePipedInpStream(): Requested sub file doesn't exists"); } 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; - } + PipedInputStream streamIn = new PipedInputStream(streamOut); - 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 = new Thread(() -> { + 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; } - }); - workerThread.start(); - return streamIn; - } - catch (IOException ioe){ - System.out.println("PFS0Provider -> getPfs0subFilePipedInpStream(): Unable to provide stream"); - return null; - } + + 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; } /** * Some sugar * */ @Override - public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){ + public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception { for (int i = 0; i < pfs0subFiles.length; i++){ if (pfs0subFiles[i].getName().equals(subFileName)) return getProviderSubFilePipedInpStream(i); diff --git a/src/main/java/konogonka/Tools/XCI/HFS0Provider.java b/src/main/java/konogonka/Tools/XCI/HFS0Provider.java index 947cf91..9e53e60 100644 --- a/src/main/java/konogonka/Tools/XCI/HFS0Provider.java +++ b/src/main/java/konogonka/Tools/XCI/HFS0Provider.java @@ -105,71 +105,65 @@ public class HFS0Provider implements ISuperProvider { public int getFilesCnt() { return filesCnt; } public boolean isPaddingHfs0() { return paddingHfs0; } public int getStringTableSize() { return stringTableSize; } - + @Override public long getRawFileDataStart() { return rawFileDataStart; } public HFS0File[] getHfs0Files() { return hfs0Files; } - @Override - public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber){ + public File getFile(){ return file; } + @Override + public PipedInputStream getProviderSubFilePipedInpStream(int subFileNumber) throws Exception{ PipedOutputStream streamOut = new PipedOutputStream(); Thread workerThread; if (subFileNumber >= hfs0Files.length) { - System.out.println("HFS0Provider -> getHfs0FilePipedInpStream(): Requested sub file doesn't exists"); - return null; + throw new Exception("HFS0Provider -> getHfs0FilePipedInpStream(): Requested sub file doesn't exists"); } - try{ - PipedInputStream streamIn = new PipedInputStream(streamOut); + 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"); + 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; } - - 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(); + streamOut.write(readBuf, 0, readPice); + readFrom += readPice; } - 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; - } + 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; } /** * Sugar * */ @Override - public PipedInputStream getProviderSubFilePipedInpStream(String subFileName){ + public PipedInputStream getProviderSubFilePipedInpStream(String subFileName) throws Exception { for (int i = 0; i < hfs0Files.length; i++){ if (hfs0Files[i].getName().equals(subFileName)) return getProviderSubFilePipedInpStream(i); diff --git a/src/main/java/konogonka/Workers/AnalyzerNCA.java b/src/main/java/konogonka/Workers/AnalyzerNCA.java index 21ffef7..5b02985 100644 --- a/src/main/java/konogonka/Workers/AnalyzerNCA.java +++ b/src/main/java/konogonka/Workers/AnalyzerNCA.java @@ -3,6 +3,7 @@ package konogonka.Workers; import javafx.concurrent.Task; import konogonka.ModelControllers.EMsgType; import konogonka.ModelControllers.LogPrinter; +import konogonka.Tools.ISuperProvider; import konogonka.Tools.NCA.NCAProvider; import java.io.File; @@ -11,11 +12,18 @@ import java.util.HashMap; public class AnalyzerNCA extends Task { private File file; + private long offset; private LogPrinter logPrinter; private HashMap keysMap; + public AnalyzerNCA(File file, HashMap keysMap){ + this(file, keysMap, 0); + } + + public AnalyzerNCA(File file, HashMap keysMap, long offset){ this.file = file; + this.offset = offset; this.logPrinter = new LogPrinter(); this.keysMap = keysMap; } @@ -27,7 +35,7 @@ public class AnalyzerNCA extends Task { NCAProvider ncaProvider; try { - ncaProvider = new NCAProvider(file, keysMap); + ncaProvider = new NCAProvider(file, keysMap, offset); }catch (Exception e){ logPrinter.print(e.getMessage(), EMsgType.FAIL); ncaProvider = null; diff --git a/src/main/java/konogonka/Workers/NspXciExtractor.java b/src/main/java/konogonka/Workers/NspXciExtractor.java index b63af2f..421b40a 100644 --- a/src/main/java/konogonka/Workers/NspXciExtractor.java +++ b/src/main/java/konogonka/Workers/NspXciExtractor.java @@ -22,9 +22,6 @@ public class NspXciExtractor extends Task { this.models = models; this.filesDestPath = filesDestPath; this.logPrinter = new LogPrinter(); - for (IRowModel model : models) { - System.out.println(model.getFileName()); - } } @Override @@ -60,8 +57,8 @@ public class NspXciExtractor extends Task { getException().printStackTrace(); // TODO: Do something with this } extractedFileBOS.close(); - } catch (IOException ioe) { - logPrinter.print("\tRead/Write error\n\t" + ioe.getMessage(), EMsgType.INFO); + } catch (Exception ioe) { + logPrinter.print("\tExtracting issue\n\t" + ioe.getMessage(), EMsgType.INFO); return null; } finally { logPrinter.print("\tEnd extracting", EMsgType.INFO); diff --git a/src/main/resources/FXML/NCA/NCATab.fxml b/src/main/resources/FXML/NCA/NCATab.fxml index 6cb6ada..bd5fd27 100644 --- a/src/main/resources/FXML/NCA/NCATab.fxml +++ b/src/main/resources/FXML/NCA/NCATab.fxml @@ -37,6 +37,7 @@ + @@ -72,7 +73,7 @@ - + - + - + - + - + - + - + - + - + - + - + - + - - + - + - - + - + @@ -437,7 +438,7 @@ - + - + - + - + - + - + @@ -579,7 +580,7 @@ - + @@ -648,6 +649,42 @@ + + + + + + + + + + + + + + + + + + + +