From 5dcbaff8ac1f15d2c8e5ea4e020ff70d7a23f99d Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sat, 24 Aug 2019 07:29:56 +0300 Subject: [PATCH] ++ NPDM implementation, UI updates --- .../java/konogonka/Child/ChildWindow.java | 24 +- .../konogonka/Controllers/ITabController.java | 1 + .../Controllers/NCA/NCAController.java | 6 +- .../Controllers/NPDM/ACI0Provider.java | 4 - .../Controllers/NPDM/ACIDProvider.java | 4 - .../Controllers/NPDM/NPDMController.java | 132 +- .../Controllers/NSP/NSPController.java | 4 + .../Controllers/TIK/TIKController.java | 6 +- .../Controllers/XCI/XCIController.java | 4 + .../Controllers/XML/XMLController.java | 7 +- .../konogonka/Tools/ASuperInFileProvider.java | 23 + .../java/konogonka/Tools/NCA/NCAProvider.java | 5 +- .../konogonka/Tools/NPDM/ACI0Provider.java | 48 + .../konogonka/Tools/NPDM/ACIDProvider.java | 70 + .../konogonka/Tools/NPDM/NPDMProvider.java | 100 +- .../Tools/PFS0/PFS0EncryptedProvider.java | 4 +- .../java/konogonka/Workers/AnalyzerNPDM.java | 15 +- .../konogonka/Workers/NspXciExtractor.java | 1 + .../FXML/NCA/NCASectionHeaderBlock.fxml | 2058 +++++++++-------- src/main/resources/FXML/NPDM/NPDMTab.fxml | 1573 +++++++++---- 20 files changed, 2630 insertions(+), 1459 deletions(-) delete mode 100644 src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java delete mode 100644 src/main/java/konogonka/Controllers/NPDM/ACIDProvider.java create mode 100644 src/main/java/konogonka/Tools/ASuperInFileProvider.java create mode 100644 src/main/java/konogonka/Tools/NPDM/ACI0Provider.java create mode 100644 src/main/java/konogonka/Tools/NPDM/ACIDProvider.java diff --git a/src/main/java/konogonka/Child/ChildWindow.java b/src/main/java/konogonka/Child/ChildWindow.java index 359ab04..16bccda 100644 --- a/src/main/java/konogonka/Child/ChildWindow.java +++ b/src/main/java/konogonka/Child/ChildWindow.java @@ -15,7 +15,7 @@ import java.util.Locale; import java.util.ResourceBundle; public class ChildWindow { - public ChildWindow(ISuperProvider provider, IRowModel model) throws IOException{ + public ChildWindow(ISuperProvider provider, IRowModel model) throws IOException { Stage stageSettings = new Stage(); stageSettings.setMinWidth(570); @@ -23,21 +23,22 @@ public class ChildWindow { FXMLLoader loaderSettings; - if (model.getFileName().endsWith(".nca")){ + if (model.getFileName().endsWith(".nca")) { loaderSettings = new FXMLLoader(getClass().getResource("/FXML/NCA/NCATab.fxml")); - } - else if(model.getFileName().endsWith(".tik")){ + } else if (model.getFileName().endsWith(".tik")) { loaderSettings = new FXMLLoader(getClass().getResource("/FXML/TIK/TIKTab.fxml")); - } - else if(model.getFileName().endsWith(".xml")){ + } else if (model.getFileName().endsWith(".xml")) { loaderSettings = new FXMLLoader(getClass().getResource("/FXML/XML/XMLTab.fxml")); } + else if(model.getFileName().endsWith(".npdm")){ + loaderSettings = new FXMLLoader(getClass().getResource("/FXML/NPDM/NPDMTab.fxml")); + } else if(model.getFileName().endsWith(".cert")){ // TODO: IMPLEMENT return; } else if(model.getFileName().endsWith(".cnmt")){ - // TODO: IMPLEMENT + // todo: implement return; } else // TODO: Dynamic detection function @@ -53,6 +54,15 @@ public class ChildWindow { XMLController myController = loaderSettings.getController(); myController.analyze(provider.getFile(), provider.getRawFileDataStart()+model.getFileOffset(), model.getFileSize()); } + else if (model.getFileName().endsWith(".npdm")){ + ITabController myController = loaderSettings.getController(); + try { + myController.analyze(provider, model.getNumber()); + } + catch (Exception e){ + System.out.println("ERR"+e.getMessage()); + } + } else { ITabController myController = loaderSettings.getController(); myController.analyze(provider.getFile(), provider.getRawFileDataStart()+model.getFileOffset()); diff --git a/src/main/java/konogonka/Controllers/ITabController.java b/src/main/java/konogonka/Controllers/ITabController.java index a9ea10e..a582dae 100644 --- a/src/main/java/konogonka/Controllers/ITabController.java +++ b/src/main/java/konogonka/Controllers/ITabController.java @@ -8,5 +8,6 @@ import java.io.File; public interface ITabController extends Initializable { void analyze(File file); void analyze(File file, long offset); + void analyze(ISuperProvider parentProvider, int fileNo) throws Exception; void resetTab(); } diff --git a/src/main/java/konogonka/Controllers/NCA/NCAController.java b/src/main/java/konogonka/Controllers/NCA/NCAController.java index 63662b1..3dbf824 100644 --- a/src/main/java/konogonka/Controllers/NCA/NCAController.java +++ b/src/main/java/konogonka/Controllers/NCA/NCAController.java @@ -5,6 +5,7 @@ import javafx.scene.control.Label; import javafx.scene.control.TextField; import konogonka.AppPreferences; import konogonka.Controllers.ITabController; +import konogonka.Tools.ISuperProvider; import konogonka.Tools.NCA.NCAContentPFS0; import konogonka.Tools.NCA.NCAProvider; import konogonka.Workers.AnalyzerNCA; @@ -105,7 +106,10 @@ public class NCAController implements ITabController { public void analyze(File file) { analyze(file, 0); } - + @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + throw new Exception("Not supported for NCA"); + } @Override public void resetTab() { // Header diff --git a/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java b/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java deleted file mode 100644 index 2fa6bc9..0000000 --- a/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java +++ /dev/null @@ -1,4 +0,0 @@ -package konogonka.Controllers.NPDM; - -public class ACI0Provider { -} diff --git a/src/main/java/konogonka/Controllers/NPDM/ACIDProvider.java b/src/main/java/konogonka/Controllers/NPDM/ACIDProvider.java deleted file mode 100644 index 9fb5584..0000000 --- a/src/main/java/konogonka/Controllers/NPDM/ACIDProvider.java +++ /dev/null @@ -1,4 +0,0 @@ -package konogonka.Controllers.NPDM; - -public class ACIDProvider { -} diff --git a/src/main/java/konogonka/Controllers/NPDM/NPDMController.java b/src/main/java/konogonka/Controllers/NPDM/NPDMController.java index f6149c3..943d58e 100644 --- a/src/main/java/konogonka/Controllers/NPDM/NPDMController.java +++ b/src/main/java/konogonka/Controllers/NPDM/NPDMController.java @@ -4,12 +4,14 @@ import javafx.fxml.FXML; import javafx.scene.control.Label; import javafx.scene.control.TextField; import konogonka.Controllers.ITabController; +import konogonka.Tools.ISuperProvider; +import konogonka.Tools.NPDM.ACI0Provider; +import konogonka.Tools.NPDM.ACIDProvider; import konogonka.Tools.NPDM.NPDMProvider; import konogonka.Workers.AnalyzerNPDM; import java.io.File; import java.net.URL; -import java.util.Locale; import java.util.ResourceBundle; import static konogonka.LoperConverter.byteArrToHexString; @@ -38,21 +40,65 @@ public class NPDMController implements ITabController { productCodeTf, reserved4Tf; + // ACI0 + @FXML + private Label aci0MagicNumLbl, + aci0Reserved1Lbl, + aci0TitleIDLbl, + aci0Reserved2Lbl, + aci0FsAccessHeaderOffsetLbl, + aci0FsAccessHeaderSizeLbl, + aci0ServiceAccessControlOffsetLbl, + aci0ServiceAccessControlSizeLbl, + aci0KernelAccessControlOffsetLbl, + aci0KernelAccessControlSizeLbl, + aci0Reserved3Lbl; + // ACID + @FXML TextField acidRsa2048signatureTf, + acidRsa2048publicKeyTf; + @FXML + private Label acidMagicNumLbl, + acidDataSizeLbl, + acidReserved1Lbl, + acidFlag1Lbl, + acidFlag2Lbl, + acidFlag3Lbl, + acidFlag4Lbl, + acidTitleRangeMinLbl, + acidTitleRangeMaxLbl, + acidFsAccessControlOffsetLbl, + acidFsAccessControlSizeLbl, + acidServiceAccessControlOffsetLbl, + acidServiceAccessControlSizeLbl, + acidKernelAccessControlOffsetLbl, + acidKernelAccessControlSizeLbl, + acidReserved2Lbl; + @Override public void initialize(URL url, ResourceBundle resourceBundle) { } @Override public void analyze(File file) { analyze(file, 0); } - + @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + AnalyzerNPDM analyzerNPDM = new AnalyzerNPDM(parentProvider, fileNo); + analyzerNPDM.setOnSucceeded(e->{ + NPDMProvider npdm = analyzerNPDM.getValue(); + setData(npdm, null); + }); + Thread workThread = new Thread(analyzerNPDM); + workThread.setDaemon(true); + workThread.start(); + } @Override public void analyze(File file, long offset) { AnalyzerNPDM analyzerNPDM = new AnalyzerNPDM(file, offset); analyzerNPDM.setOnSucceeded(e->{ - NPDMProvider tik = analyzerNPDM.getValue(); + NPDMProvider npdm = analyzerNPDM.getValue(); if (offset == 0) - setData(tik, file); + setData(npdm, file); else - setData(tik, null); + setData(npdm, null); }); Thread workThread = new Thread(analyzerNPDM); workThread.setDaemon(true); @@ -79,6 +125,38 @@ public class NPDMController implements ITabController { productCodeTf.setText("-"); reserved4Tf.setText("-"); npdmFileSize.setText("-"); + + // ACI0 + aci0MagicNumLbl.setText("-"); + aci0Reserved1Lbl.setText("-"); + aci0TitleIDLbl.setText("-"); + aci0Reserved2Lbl.setText("-"); + aci0FsAccessHeaderOffsetLbl.setText("-"); + aci0FsAccessHeaderSizeLbl.setText("-"); + aci0ServiceAccessControlOffsetLbl.setText("-"); + aci0ServiceAccessControlSizeLbl.setText("-"); + aci0KernelAccessControlOffsetLbl.setText("-"); + aci0KernelAccessControlSizeLbl.setText("-"); + aci0Reserved3Lbl.setText("-"); + // ACID + acidRsa2048signatureTf.setText("-"); + acidRsa2048publicKeyTf.setText("-"); + acidMagicNumLbl.setText("-"); + acidDataSizeLbl.setText("-"); + acidReserved1Lbl.setText("-"); + acidFlag1Lbl.setText("-"); + acidFlag2Lbl.setText("-"); + acidFlag3Lbl.setText("-"); + acidFlag4Lbl.setText("-"); + acidTitleRangeMinLbl.setText("-"); + acidTitleRangeMaxLbl.setText("-"); + acidFsAccessControlOffsetLbl.setText("-"); + acidFsAccessControlSizeLbl.setText("-"); + acidServiceAccessControlOffsetLbl.setText("-"); + acidServiceAccessControlSizeLbl.setText("-"); + acidKernelAccessControlOffsetLbl.setText("-"); + acidKernelAccessControlSizeLbl.setText("-"); + acidReserved2Lbl.setText("-"); } private void setData(NPDMProvider npdmProvider, File file) { if (npdmProvider == null) @@ -101,10 +179,42 @@ public class NPDMController implements ITabController { titleNameTf.setText(npdmProvider.getTitleName()); productCodeTf.setText(byteArrToHexString(npdmProvider.getProductCode())); reserved4Tf.setText(byteArrToHexString(npdmProvider.getReserved4())); - aci0offsetLbl.setText(Long.toString(npdmProvider.getAci0offset())); - aci0sizeLbl.setText(Long.toString(npdmProvider.getAci0size())); - acidOffsetLbl.setText(Long.toString(npdmProvider.getAcidOffset())); - acidSizeLbl.setText(Long.toString(npdmProvider.getAcidSize())); + aci0offsetLbl.setText(Integer.toString(npdmProvider.getAci0offset())); + aci0sizeLbl.setText(Integer.toString(npdmProvider.getAci0size())); + acidOffsetLbl.setText(Integer.toString(npdmProvider.getAcidOffset())); + acidSizeLbl.setText(Integer.toString(npdmProvider.getAcidSize())); + // ACI0 + ACI0Provider aci0 = npdmProvider.getAci0(); + aci0MagicNumLbl.setText(aci0.getMagicNum()); + aci0Reserved1Lbl.setText(byteArrToHexString(aci0.getReserved1())); + aci0TitleIDLbl.setText(byteArrToHexString(aci0.getTitleID())); + aci0Reserved2Lbl.setText(byteArrToHexString(aci0.getReserved2())); + aci0FsAccessHeaderOffsetLbl.setText(Integer.toString(aci0.getFsAccessHeaderOffset())); + aci0FsAccessHeaderSizeLbl.setText(Integer.toString(aci0.getFsAccessHeaderSize())); + aci0ServiceAccessControlOffsetLbl.setText(Integer.toString(aci0.getServiceAccessControlOffset())); + aci0ServiceAccessControlSizeLbl.setText(Integer.toString(aci0.getServiceAccessControlSize())); + aci0KernelAccessControlOffsetLbl.setText(Integer.toString(aci0.getKernelAccessControlOffset())); + aci0KernelAccessControlSizeLbl.setText(Integer.toString(aci0.getKernelAccessControlSize())); + aci0Reserved3Lbl.setText(byteArrToHexString(aci0.getReserved3())); + // ACID + ACIDProvider acid = npdmProvider.getAcid(); + acidRsa2048signatureTf.setText(byteArrToHexString(acid.getRsa2048signature())); + acidRsa2048publicKeyTf.setText(byteArrToHexString(acid.getRsa2048publicKey())); + acidMagicNumLbl.setText(acid.getMagicNum()); + acidDataSizeLbl.setText(Integer.toString(acid.getDataSize())); + acidReserved1Lbl.setText(byteArrToHexString(acid.getReserved1())); + acidFlag1Lbl.setText(String.format("0x%02x", acid.getFlag1())); + acidFlag2Lbl.setText(String.format("0x%02x", acid.getFlag2())); + acidFlag3Lbl.setText(String.format("0x%02x", acid.getFlag3())); + acidFlag4Lbl.setText(String.format("0x%02x", acid.getFlag4())); + acidTitleRangeMinLbl.setText(Long.toString(acid.getTitleRangeMin())); + acidTitleRangeMaxLbl.setText(Long.toString(acid.getTitleRangeMax())); + acidFsAccessControlOffsetLbl.setText(Integer.toString(acid.getFsAccessControlOffset())); + acidFsAccessControlSizeLbl.setText(Integer.toString(acid.getFsAccessControlSize())); + acidServiceAccessControlOffsetLbl.setText(Integer.toString(acid.getServiceAccessControlOffset())); + acidServiceAccessControlSizeLbl.setText(Integer.toString(acid.getServiceAccessControlSize())); + acidKernelAccessControlOffsetLbl.setText(Integer.toString(acid.getKernelAccessControlOffset())); + acidKernelAccessControlSizeLbl.setText(Integer.toString(acid.getKernelAccessControlSize())); + acidReserved2Lbl.setText(byteArrToHexString(acid.getReserved2())); } - -} +} \ No newline at end of file diff --git a/src/main/java/konogonka/Controllers/NSP/NSPController.java b/src/main/java/konogonka/Controllers/NSP/NSPController.java index b947646..d285878 100644 --- a/src/main/java/konogonka/Controllers/NSP/NSPController.java +++ b/src/main/java/konogonka/Controllers/NSP/NSPController.java @@ -108,6 +108,10 @@ public class NSPController implements ITabController { workThread.setDaemon(true); workThread.start(); } + @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + throw new Exception("Not supported for NSP"); + } /** * Just populate fields by already analyzed PFS0 * */ diff --git a/src/main/java/konogonka/Controllers/TIK/TIKController.java b/src/main/java/konogonka/Controllers/TIK/TIKController.java index 394bfe0..f85f9f4 100644 --- a/src/main/java/konogonka/Controllers/TIK/TIKController.java +++ b/src/main/java/konogonka/Controllers/TIK/TIKController.java @@ -6,6 +6,7 @@ import javafx.scene.control.Label; import javafx.scene.control.TextField; import konogonka.AppPreferences; import konogonka.Controllers.ITabController; +import konogonka.Tools.ISuperProvider; import konogonka.Tools.TIK.TIKProvider; import konogonka.Workers.AnalyzerTIK; @@ -61,7 +62,10 @@ public class TIKController implements ITabController { @Override public void analyze(File file) { analyze(file, 0); } - + @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + throw new Exception("Not supported for TIK"); + } @Override public void analyze(File file, long offset) { AnalyzerTIK analyzerTIK = new AnalyzerTIK(file, offset); diff --git a/src/main/java/konogonka/Controllers/XCI/XCIController.java b/src/main/java/konogonka/Controllers/XCI/XCIController.java index 3b31c50..aefbb6d 100644 --- a/src/main/java/konogonka/Controllers/XCI/XCIController.java +++ b/src/main/java/konogonka/Controllers/XCI/XCIController.java @@ -107,6 +107,10 @@ public class XCIController implements ITabController { workThread.start(); } @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + throw new Exception("Not supported for XCI"); + } + @Override public void resetTab(){ HFSBlockController.setSelectedFile(null); /* Header */ diff --git a/src/main/java/konogonka/Controllers/XML/XMLController.java b/src/main/java/konogonka/Controllers/XML/XMLController.java index 97cb398..aed90d9 100644 --- a/src/main/java/konogonka/Controllers/XML/XMLController.java +++ b/src/main/java/konogonka/Controllers/XML/XMLController.java @@ -4,6 +4,7 @@ import javafx.fxml.FXML; import javafx.scene.control.TextArea; import konogonka.Controllers.ITabController; import konogonka.MediatorControl; +import konogonka.Tools.ISuperProvider; import java.io.BufferedInputStream; import java.io.File; @@ -14,7 +15,6 @@ import java.util.ResourceBundle; public class XMLController implements ITabController { @FXML - private TextArea mainTa; @Override @@ -47,6 +47,7 @@ public class XMLController implements ITabController { MediatorControl.getInstance().getContoller().logArea.appendText("XMLController -> analyze(): \n"+e.getMessage()); } } + /** * Read from offset to length * */ @@ -68,6 +69,10 @@ public class XMLController implements ITabController { } } @Override + public void analyze(ISuperProvider parentProvider, int fileNo) throws Exception { + throw new Exception("Not supported for XML"); + } + @Override public void resetTab() { mainTa.setText(null); } diff --git a/src/main/java/konogonka/Tools/ASuperInFileProvider.java b/src/main/java/konogonka/Tools/ASuperInFileProvider.java new file mode 100644 index 0000000..7813a09 --- /dev/null +++ b/src/main/java/konogonka/Tools/ASuperInFileProvider.java @@ -0,0 +1,23 @@ +package konogonka.Tools; + +import java.io.IOException; +import java.io.PipedInputStream; + +/** + * Create prototype of the provider that created and works with pipes only + * */ +public abstract class ASuperInFileProvider { + protected byte[] readFromStream(PipedInputStream pis, int size) throws IOException { + byte[] buffer = new byte[size]; + int startingPos = 0; + int readCnt; + while (size > 0){ + readCnt = pis.read(buffer, startingPos, size); + if (readCnt == -1) + return null; + startingPos += readCnt; + size -= readCnt; + } + return buffer; + } +} diff --git a/src/main/java/konogonka/Tools/NCA/NCAProvider.java b/src/main/java/konogonka/Tools/NCA/NCAProvider.java index bf76293..4962972 100644 --- a/src/main/java/konogonka/Tools/NCA/NCAProvider.java +++ b/src/main/java/konogonka/Tools/NCA/NCAProvider.java @@ -163,7 +163,7 @@ public class NCAProvider { else cryptoTypeReal = cryptoType1; - if (cryptoTypeReal > 0) // TODO: CLARIFY WHY THEH FUCK IS IT FAIR???? + if (cryptoTypeReal > 0) // TODO: CLARIFY WHY THE FUCK IS IT FAIR???? cryptoTypeReal -= 1; //todo: if nca3 proceed @@ -193,6 +193,8 @@ public class NCAProvider { decryptedKey2 = cipher.doFinal(encryptedKey2); decryptedKey3 = cipher.doFinal(encryptedKey3); } + else + throw new Exception("key_are_key_[UNKNOWN] requested ("+keyIndex+"). Not supported."); } tableEntry0 = new NCAHeaderTableEntry(tableBytes); @@ -244,6 +246,7 @@ public class NCAProvider { public NCASectionBlock getSectionBlock1() { return sectionBlock1; } public NCASectionBlock getSectionBlock2() { return sectionBlock2; } public NCASectionBlock getSectionBlock3() { return sectionBlock3; } + public boolean isKeyAvailable(){ // TODO: USE if (Arrays.equals(rightsId, new byte[0x10])) return true; diff --git a/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java b/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java new file mode 100644 index 0000000..4c8bd0f --- /dev/null +++ b/src/main/java/konogonka/Tools/NPDM/ACI0Provider.java @@ -0,0 +1,48 @@ +package konogonka.Tools.NPDM; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import static konogonka.LoperConverter.getLEint; + +public class ACI0Provider { + private String magicNum; + private byte[] reserved1; + private byte[] titleID; + private byte[] reserved2; + private int fsAccessHeaderOffset; + private int fsAccessHeaderSize; + private int serviceAccessControlOffset; + private int serviceAccessControlSize; + private int kernelAccessControlOffset; + private int kernelAccessControlSize; + private byte[] reserved3; + + public ACI0Provider(byte[] aci0bytes) throws Exception{ + if (aci0bytes.length < 0x40) + throw new Exception("ACI0 size is too short"); + magicNum = new String(aci0bytes, 0, 0x4, StandardCharsets.UTF_8); + reserved1 = Arrays.copyOfRange(aci0bytes, 0x4, 0x10); + titleID = Arrays.copyOfRange(aci0bytes, 0x10, 0x18); + reserved2 = Arrays.copyOfRange(aci0bytes, 0x18, 0x20); + fsAccessHeaderOffset = getLEint(aci0bytes, 0x20); + fsAccessHeaderSize = getLEint(aci0bytes, 0x24); + serviceAccessControlOffset = getLEint(aci0bytes, 0x28); + serviceAccessControlSize = getLEint(aci0bytes, 0x2C); + kernelAccessControlOffset = getLEint(aci0bytes, 0x30); + kernelAccessControlSize = getLEint(aci0bytes, 0x34); + reserved3 = Arrays.copyOfRange(aci0bytes, 0x38, 0x40); + } + + public String getMagicNum() { return magicNum; } + public byte[] getReserved1() { return reserved1; } + public byte[] getTitleID() { return titleID; } + public byte[] getReserved2() { return reserved2; } + public int getFsAccessHeaderOffset() { return fsAccessHeaderOffset; } + public int getFsAccessHeaderSize() { return fsAccessHeaderSize; } + public int getServiceAccessControlOffset() { return serviceAccessControlOffset; } + public int getServiceAccessControlSize() { return serviceAccessControlSize; } + public int getKernelAccessControlOffset() { return kernelAccessControlOffset; } + public int getKernelAccessControlSize() { return kernelAccessControlSize; } + public byte[] getReserved3() { return reserved3; } +} \ No newline at end of file diff --git a/src/main/java/konogonka/Tools/NPDM/ACIDProvider.java b/src/main/java/konogonka/Tools/NPDM/ACIDProvider.java new file mode 100644 index 0000000..c1f68fe --- /dev/null +++ b/src/main/java/konogonka/Tools/NPDM/ACIDProvider.java @@ -0,0 +1,70 @@ +package konogonka.Tools.NPDM; + +import java.nio.charset.StandardCharsets; +import java.util.Arrays; + +import static konogonka.LoperConverter.*; + +public class ACIDProvider { + + private byte[] rsa2048signature; + private byte[] rsa2048publicKey; + private String magicNum; + private int dataSize; + private byte[] reserved1; + private byte flag1; + private byte flag2; + private byte flag3; + private byte flag4; + private long titleRangeMin; + private long titleRangeMax; + private int fsAccessControlOffset; + private int fsAccessControlSize; + private int serviceAccessControlOffset; + private int serviceAccessControlSize; + private int kernelAccessControlOffset; + private int kernelAccessControlSize; + private byte[] reserved2; + + public ACIDProvider(byte[] acidBytes) throws Exception{ + if (acidBytes.length < 0x240) + throw new Exception("ACI0 size is too short"); + rsa2048signature = Arrays.copyOfRange(acidBytes, 0, 0x100); + rsa2048publicKey = Arrays.copyOfRange(acidBytes, 0x100, 0x200); + magicNum = new String(acidBytes, 0x200, 0x4, StandardCharsets.UTF_8); + dataSize = getLEint(acidBytes, 0x204); + reserved1 = Arrays.copyOfRange(acidBytes, 0x208, 0x20C); + flag1 = acidBytes[0x20C]; + flag2 = acidBytes[0x20D]; + flag3 = acidBytes[0x20E]; + flag4 = acidBytes[0x20F]; + titleRangeMin = getLElong(acidBytes, 0x210); + titleRangeMax = getLElong(acidBytes, 0x218); + fsAccessControlOffset = getLEint(acidBytes, 0x220); + fsAccessControlSize = getLEint(acidBytes, 0x224); + serviceAccessControlOffset = getLEint(acidBytes, 0x228); + serviceAccessControlSize = getLEint(acidBytes, 0x22C); + kernelAccessControlOffset = getLEint(acidBytes, 0x230); + kernelAccessControlSize = getLEint(acidBytes, 0x234); + reserved2 = Arrays.copyOfRange(acidBytes, 0x238, 0x240); + } + + public byte[] getRsa2048signature() { return rsa2048signature; } + public byte[] getRsa2048publicKey() { return rsa2048publicKey; } + public String getMagicNum() { return magicNum; } + public int getDataSize() { return dataSize; } + public byte[] getReserved1() { return reserved1; } + public byte getFlag1() { return flag1; } + public byte getFlag2() { return flag2; } + public byte getFlag3() { return flag3; } + public byte getFlag4() { return flag4; } + public long getTitleRangeMin() { return titleRangeMin; } + public long getTitleRangeMax() { return titleRangeMax; } + public int getFsAccessControlOffset() { return fsAccessControlOffset; } + public int getFsAccessControlSize() { return fsAccessControlSize; } + public int getServiceAccessControlOffset() { return serviceAccessControlOffset; } + public int getServiceAccessControlSize() { return serviceAccessControlSize; } + public int getKernelAccessControlOffset() { return kernelAccessControlOffset; } + public int getKernelAccessControlSize() { return kernelAccessControlSize; } + public byte[] getReserved2() { return reserved2; } +} \ No newline at end of file diff --git a/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java b/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java index 95c1d19..a15763a 100644 --- a/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java +++ b/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java @@ -1,13 +1,14 @@ package konogonka.Tools.NPDM; -import java.io.File; -import java.io.RandomAccessFile; +import konogonka.Tools.ASuperInFileProvider; + +import java.io.*; import java.nio.charset.StandardCharsets; import java.util.Arrays; import static konogonka.LoperConverter.*; -public class NPDMProvider { +public class NPDMProvider extends ASuperInFileProvider { private String magicNum; private byte[] reserved1; @@ -22,12 +23,63 @@ public class NPDMProvider { private String titleName; private byte[] productCode; private byte[] reserved4; - private long aci0offset; // originally 4-bytes (u-int) - private long aci0size; // originally 4-bytes (u-int) - private long acidOffset; // originally 4-bytes (u-int) - private long acidSize; // originally 4-bytes (u-int) + private int aci0offset; // originally 4-bytes (u-int) + private int aci0size; // originally 4-bytes (u-int) + private int acidOffset; // originally 4-bytes (u-int) + private int acidSize; // originally 4-bytes (u-int) - public NPDMProvider(File file) throws Exception { this(file, 0); } + private ACI0Provider aci0; + private ACIDProvider acid; + + public NPDMProvider(PipedInputStream pis) throws Exception{ + byte[] mainBuf = new byte[0x80]; + if(pis.read(mainBuf) != 0x80) + throw new Exception("NPDMProvider: Failed to read 'META'"); + aci0offset = getLEint(mainBuf, 0x70); + aci0size = getLEint(mainBuf, 0x74); + acidOffset = getLEint(mainBuf, 0x78); + acidSize = getLEint(mainBuf, 0x7C); + byte[] aci0Buf; + byte[] acidBuf; + if (aci0offset < acidOffset){ + if (pis.skip(aci0offset - 0x80) != (aci0offset - 0x80)) + throw new Exception("NPDMProvider: Failed to skip bytes till 'ACI0'"); + if ((aci0Buf = readFromStream(pis, aci0size)) == null) + throw new Exception("NPDMProvider: Failed to read 'ACI0'"); + if (pis.skip(acidOffset - aci0offset - aci0size) != (acidOffset - aci0offset - aci0size)) + throw new Exception("NPDMProvider: Failed to skip bytes till 'ACID'"); + if ((acidBuf = readFromStream(pis, acidSize)) == null) + throw new Exception("NPDMProvider: Failed to read 'ACID'"); + } + else { + if (pis.skip(acidOffset - 0x80) != (acidOffset - 0x80)) + throw new Exception("NPDMProvider: Failed to skip bytes till 'ACID'"); + if ((acidBuf = readFromStream(pis, acidSize)) == null) + throw new Exception("NPDMProvider: Failed to read 'ACID'"); + if (pis.skip(aci0offset - acidOffset - acidSize) != (aci0offset - acidOffset - acidSize)) + throw new Exception("NPDMProvider: Failed to skip bytes till 'ACI0'"); + if ((aci0Buf = readFromStream(pis, aci0size)) == null) + throw new Exception("NPDMProvider: Failed to read 'ACI0'"); + } + magicNum = new String(mainBuf, 0, 4, StandardCharsets.UTF_8); + reserved1 = Arrays.copyOfRange(mainBuf, 0x4, 0xC); + MMUFlags = mainBuf[0xC]; + reserved2 = mainBuf[0xD]; + mainThreadPrio = mainBuf[0xE]; + mainThreadCoreNum = mainBuf[0xF]; + reserved3 = Arrays.copyOfRange(mainBuf, 0x10, 0x14); + personalMmHeapSize = getLEint(mainBuf, 0x14); + version = getLEint(mainBuf, 0x18); + mainThreadStackSize = getLElongOfInt(mainBuf, 0x1C); + titleName = new String(mainBuf, 0x20, 0x10, StandardCharsets.UTF_8); + productCode = Arrays.copyOfRange(mainBuf, 0x30, 0x40); + reserved4 = Arrays.copyOfRange(mainBuf, 0x40, 0x70); + + aci0 = new ACI0Provider(aci0Buf); + acid = new ACIDProvider(acidBuf); + } + + public NPDMProvider(File file) throws Exception { this(file, 0); } public NPDMProvider(File file, long offset) throws Exception { if (file.length() - offset < 0x80) // Header's size @@ -51,10 +103,23 @@ public class NPDMProvider { titleName = new String(metaBuf, 0x20, 0x10, StandardCharsets.UTF_8); productCode = Arrays.copyOfRange(metaBuf, 0x30, 0x40); reserved4 = Arrays.copyOfRange(metaBuf, 0x40, 0x70); - aci0offset = getLElongOfInt(metaBuf, 0x70); - aci0size = getLElongOfInt(metaBuf, 0x74); - acidOffset = getLElongOfInt(metaBuf, 0x78); - acidSize = getLElongOfInt(metaBuf, 0x7C); + aci0offset = getLEint(metaBuf, 0x70); + aci0size = getLEint(metaBuf, 0x74); + acidOffset = getLEint(metaBuf, 0x78); + acidSize = getLEint(metaBuf, 0x7C); + // Get ACI0 + raf.seek(aci0offset); + metaBuf = new byte[aci0size]; // TODO: NOTE: we read all size but need only header + if (raf.read(metaBuf) != aci0size) + throw new Exception("NPDMProvider: Failed to read 'ACI0'"); + aci0 = new ACI0Provider(metaBuf); + // Get ACID + raf.seek(acidOffset); + metaBuf = new byte[acidSize]; // TODO: NOTE: we read all size but need only header + if (raf.read(metaBuf) != acidSize) + throw new Exception("NPDMProvider: Failed to read 'ACID'"); + acid = new ACIDProvider(metaBuf); + raf.close(); } public String getMagicNum() { return magicNum; } @@ -70,8 +135,11 @@ public class NPDMProvider { public String getTitleName() { return titleName; } public byte[] getProductCode() { return productCode; } public byte[] getReserved4() { return reserved4; } - public long getAci0offset() { return aci0offset; } - public long getAci0size() { return aci0size; } - public long getAcidOffset() { return acidOffset; } - public long getAcidSize() { return acidSize; } + public int getAci0offset() { return aci0offset; } + public int getAci0size() { return aci0size; } + public int getAcidOffset() { return acidOffset; } + public int getAcidSize() { return acidSize; } + + public ACI0Provider getAci0() { return aci0; } + public ACIDProvider getAcid() { return acid; } } diff --git a/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java b/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java index 4848fe5..d36a215 100644 --- a/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java +++ b/src/main/java/konogonka/Tools/PFS0/PFS0EncryptedProvider.java @@ -245,7 +245,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{ streamOut.write(dectyptedBlock, 0, extraData); } else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock"); + System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from bock"); return; } } @@ -256,7 +256,7 @@ public class PFS0EncryptedProvider implements IPFS0Provider{ streamOut.write(dectyptedBlock, 0, 0x200 + extraData); } else { - System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from 1st bock"); + System.out.println("PFS0EncryptedProvider -> getProviderSubFilePipedInpStream(): Unable to get 512 bytes from last bock"); return; } } diff --git a/src/main/java/konogonka/Workers/AnalyzerNPDM.java b/src/main/java/konogonka/Workers/AnalyzerNPDM.java index 4745e6e..a45b1a4 100644 --- a/src/main/java/konogonka/Workers/AnalyzerNPDM.java +++ b/src/main/java/konogonka/Workers/AnalyzerNPDM.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.NPDM.NPDMProvider; import java.io.File; @@ -13,6 +14,15 @@ public class AnalyzerNPDM extends Task { private long offset; private LogPrinter logPrinter; + private ISuperProvider parentProvider; + private int fileNo; + + public AnalyzerNPDM(ISuperProvider parentProvider, int fileNo){ + this.parentProvider = parentProvider; + this.fileNo = fileNo; + this.logPrinter = new LogPrinter(); + } + public AnalyzerNPDM(File file){ this(file, 0); } @@ -27,7 +37,10 @@ public class AnalyzerNPDM extends Task { protected NPDMProvider call() { logPrinter.print("\tStart chain: NPDM", EMsgType.INFO); try{ - return new NPDMProvider(file, offset); + if (parentProvider != null) + return new NPDMProvider(parentProvider.getProviderSubFilePipedInpStream(fileNo)); + else + return new NPDMProvider(file, offset); } catch (Exception e){ logPrinter.print("\tException: "+e.getMessage(), EMsgType.FAIL); diff --git a/src/main/java/konogonka/Workers/NspXciExtractor.java b/src/main/java/konogonka/Workers/NspXciExtractor.java index 1ba3415..19756f9 100644 --- a/src/main/java/konogonka/Workers/NspXciExtractor.java +++ b/src/main/java/konogonka/Workers/NspXciExtractor.java @@ -44,6 +44,7 @@ public class NspXciExtractor extends Task { readBuf = new byte[0x800000]; //*** PROGRESS BAR DECORCATIONS START progressHandleFRead += readSize; + System.out.println(readSize); try { logPrinter.updateProgress((progressHandleFRead)/(progressHandleFSize/100.0) / 100.0); }catch (InterruptedException ie){ diff --git a/src/main/resources/FXML/NCA/NCASectionHeaderBlock.fxml b/src/main/resources/FXML/NCA/NCASectionHeaderBlock.fxml index a02b125..685cdbb 100644 --- a/src/main/resources/FXML/NCA/NCASectionHeaderBlock.fxml +++ b/src/main/resources/FXML/NCA/NCASectionHeaderBlock.fxml @@ -12,12 +12,12 @@ - + - - + + @@ -293,710 +293,715 @@ + + + + + + diff --git a/src/main/resources/FXML/NPDM/NPDMTab.fxml b/src/main/resources/FXML/NPDM/NPDMTab.fxml index 75a8095..7c79b0d 100644 --- a/src/main/resources/FXML/NPDM/NPDMTab.fxml +++ b/src/main/resources/FXML/NPDM/NPDMTab.fxml @@ -4,6 +4,7 @@ + @@ -14,427 +15,1167 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - +