diff --git a/README.md b/README.md
index 98b56b0..75a78d4 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# konogonka
-Deep WIP multi-tool to work with XCI/NSP/NCA/NRO(?) files
+Deep WIP multi-tool to work with NS-specific files / filesystem images.
### License
@@ -24,5 +24,6 @@ JRE/JDK 8u60 or higher.
### Checklist
* [ ] LogPrinter to singleton implementation
+* [ ] NPDM support
* [ ] CNMT support
-* [ ] support
\ No newline at end of file
+* [ ] NSO support
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 88a71f2..b5be10a 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,11 +8,11 @@
konogonka
konogonka
- 0.1-SNAPSHOT
+ 0.0-SNAPSHOT
https://github.com/developersu/${project.name}}/
- NSP and XCI multitool
+ NS filesystem multitool
2019
diff --git a/src/main/java/konogonka/Controllers/MainController.java b/src/main/java/konogonka/Controllers/MainController.java
index 7e10a2e..d9e79eb 100644
--- a/src/main/java/konogonka/Controllers/MainController.java
+++ b/src/main/java/konogonka/Controllers/MainController.java
@@ -8,6 +8,7 @@ import javafx.stage.FileChooser;
import konogonka.AppPreferences;
import konogonka.Child.ChildWindow;
import konogonka.Controllers.NCA.NCAController;
+import konogonka.Controllers.NPDM.NPDMController;
import konogonka.Controllers.NSP.NSPController;
import konogonka.Controllers.TIK.TIKController;
import konogonka.Controllers.XCI.XCIController;
@@ -50,6 +51,8 @@ public class MainController implements Initializable {
private TIKController TIKTabController;
@FXML
private XMLController XMLTabController;
+ @FXML
+ private NPDMController NPDMTabController;
private File selectedFile;
@@ -83,7 +86,7 @@ public class MainController implements Initializable {
else
fileChooser.setInitialDirectory(new File(System.getProperty("user.home")));
- fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NS files", "*.nsp", "*.xci", "*.nca", "*.tik", "*.xml"));
+ fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NS files", "*.nsp", "*.xci", "*.nca", "*.tik", "*.xml", "*.npdm"));
this.selectedFile = fileChooser.showOpenDialog(analyzeBtn.getScene().getWindow());
@@ -93,6 +96,7 @@ public class MainController implements Initializable {
NCATabController.resetTab();
TIKTabController.resetTab();
XMLTabController.resetTab();
+ NPDMTabController.resetTab();
if (this.selectedFile != null && this.selectedFile.exists()) {
filenameSelected.setText(this.selectedFile.getAbsolutePath());
@@ -108,6 +112,8 @@ public class MainController implements Initializable {
tabPane.getSelectionModel().select(3);
else if (this.selectedFile.getName().toLowerCase().endsWith(".xml"))
tabPane.getSelectionModel().select(4);
+ else if (this.selectedFile.getName().toLowerCase().endsWith(".npdm"))
+ tabPane.getSelectionModel().select(5);
}
logArea.clear();
@@ -126,6 +132,8 @@ public class MainController implements Initializable {
TIKTabController.analyze(selectedFile);
else if (selectedFile.getName().toLowerCase().endsWith("xml"))
XMLTabController.analyze(selectedFile);
+ else if (selectedFile.getName().toLowerCase().endsWith("npdm"))
+ NPDMTabController.analyze(selectedFile);
}
@FXML
private void showHideLogs(){
diff --git a/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java b/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java
new file mode 100644
index 0000000..2fa6bc9
--- /dev/null
+++ b/src/main/java/konogonka/Controllers/NPDM/ACI0Provider.java
@@ -0,0 +1,4 @@
+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
new file mode 100644
index 0000000..9fb5584
--- /dev/null
+++ b/src/main/java/konogonka/Controllers/NPDM/ACIDProvider.java
@@ -0,0 +1,4 @@
+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
new file mode 100644
index 0000000..f6149c3
--- /dev/null
+++ b/src/main/java/konogonka/Controllers/NPDM/NPDMController.java
@@ -0,0 +1,110 @@
+package konogonka.Controllers.NPDM;
+
+import javafx.fxml.FXML;
+import javafx.scene.control.Label;
+import javafx.scene.control.TextField;
+import konogonka.Controllers.ITabController;
+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;
+
+public class NPDMController implements ITabController {
+
+ @FXML
+ private Label magicNumLbl,
+ reserved1Lbl,
+ MMUFlagsLbl,
+ reserved2Lbl,
+ mainThreadPrioLbl,
+ mainThreadCoreNumLbl,
+ reserved3Lbl,
+ personalMmHeapSizeLbl,
+ versionLbl,
+ mainThreadStackSizeLbl,
+ aci0offsetLbl,
+ aci0sizeLbl,
+ acidOffsetLbl,
+ acidSizeLbl,
+ npdmFileSize;
+
+ @FXML
+ private TextField titleNameTf,
+ productCodeTf,
+ reserved4Tf;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) { }
+
+ @Override
+ public void analyze(File file) { analyze(file, 0); }
+
+ @Override
+ public void analyze(File file, long offset) {
+ AnalyzerNPDM analyzerNPDM = new AnalyzerNPDM(file, offset);
+ analyzerNPDM.setOnSucceeded(e->{
+ NPDMProvider tik = analyzerNPDM.getValue();
+ if (offset == 0)
+ setData(tik, file);
+ else
+ setData(tik, null);
+ });
+ Thread workThread = new Thread(analyzerNPDM);
+ workThread.setDaemon(true);
+ workThread.start();
+ }
+
+ @Override
+ public void resetTab() {
+ magicNumLbl.setText("-");
+ reserved1Lbl.setText("-");
+ MMUFlagsLbl.setText("-");
+ reserved2Lbl.setText("-");
+ mainThreadPrioLbl.setText("-");
+ mainThreadCoreNumLbl.setText("-");
+ reserved3Lbl.setText("-");
+ personalMmHeapSizeLbl.setText("-");
+ versionLbl.setText("-");
+ mainThreadStackSizeLbl.setText("-");
+ aci0offsetLbl.setText("-");
+ aci0sizeLbl.setText("-");
+ acidOffsetLbl.setText("-");
+ acidSizeLbl.setText("-");
+ titleNameTf.setText("-");
+ productCodeTf.setText("-");
+ reserved4Tf.setText("-");
+ npdmFileSize.setText("-");
+ }
+ private void setData(NPDMProvider npdmProvider, File file) {
+ if (npdmProvider == null)
+ return;
+ if (file != null)
+ npdmFileSize.setText(Long.toString(file.length()));
+ else
+ npdmFileSize.setText("skipping calculation for in-file ticket");
+
+ magicNumLbl.setText(npdmProvider.getMagicNum());
+ reserved1Lbl.setText(byteArrToHexString(npdmProvider.getReserved1()));
+ MMUFlagsLbl.setText(npdmProvider.getMMUFlags()+" (0b"+String.format("%8s", Integer.toBinaryString(npdmProvider.getMMUFlags() & 0xFF)).replace(' ', '0')+")");
+ reserved2Lbl.setText(String.format("0x%02x", npdmProvider.getReserved2()));
+ mainThreadPrioLbl.setText(Byte.toString(npdmProvider.getMainThreadPrio()));
+ mainThreadCoreNumLbl.setText(Byte.toString(npdmProvider.getMainThreadCoreNum()));
+ reserved3Lbl.setText(byteArrToHexString(npdmProvider.getReserved3()));
+ personalMmHeapSizeLbl.setText(Integer.toString(npdmProvider.getPersonalMmHeapSize()));
+ versionLbl.setText(Integer.toString(npdmProvider.getVersion()));
+ mainThreadStackSizeLbl.setText(Long.toString(npdmProvider.getMainThreadStackSize()));
+ 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()));
+ }
+
+}
diff --git a/src/main/java/konogonka/Controllers/TIK/TIKController.java b/src/main/java/konogonka/Controllers/TIK/TIKController.java
index 855d29b..394bfe0 100644
--- a/src/main/java/konogonka/Controllers/TIK/TIKController.java
+++ b/src/main/java/konogonka/Controllers/TIK/TIKController.java
@@ -48,9 +48,7 @@ public class TIKController implements ITabController {
String key = rightsIdTf.getText();
String value = titleKeyBlockStartTf.getText();
int titleKeysCnt = AppPreferences.getInstance().getTitleKeysCount();
- System.out.println(key+" "+value+" "+titleKeysCnt);
if (key.length() > 16 && ! (key.length() > 32) && value.length() == 32){
- System.out.println("OK");
for (int i = 0; i < titleKeysCnt; i++){
if (AppPreferences.getInstance().getTitleKeyPair(i)[0].equals(key))
return;
diff --git a/src/main/java/konogonka/Controllers/XML/XMLController.java b/src/main/java/konogonka/Controllers/XML/XMLController.java
index d1d0f89..97cb398 100644
--- a/src/main/java/konogonka/Controllers/XML/XMLController.java
+++ b/src/main/java/konogonka/Controllers/XML/XMLController.java
@@ -51,7 +51,6 @@ public class XMLController implements ITabController {
* Read from offset to length
* */
public void analyze(File file, long offset, long fileSize) {
- System.out.println(file.length()+" "+offset+" "+fileSize);
try {
if (fileSize > 10485760) // 10mB
throw new Exception("XMLController -> analyze(): File is too big. It must be something wrong with it. Usually they're smaller");
diff --git a/src/main/java/konogonka/LoperConverter.java b/src/main/java/konogonka/LoperConverter.java
index 6ee0448..2076030 100644
--- a/src/main/java/konogonka/LoperConverter.java
+++ b/src/main/java/konogonka/LoperConverter.java
@@ -11,6 +11,17 @@ public class LoperConverter {
public static long getLElong(byte[] bytes, int fromOffset){
return ByteBuffer.wrap(bytes, fromOffset, 0x8).order(ByteOrder.LITTLE_ENDIAN).getLong();
}
+ /**
+ * Convert int to long. Workaround to store unsigned int
+ * @param bytes original array
+ * @param fromOffset start position of the 4-bytes value
+ * */
+ public static long getLElongOfInt(byte[] bytes, int fromOffset){
+ final byte[] holder = new byte[8];
+ System.arraycopy(bytes, fromOffset, holder, 0, 4);
+ return ByteBuffer.wrap(holder).order(ByteOrder.LITTLE_ENDIAN).getLong();
+ }
+
public static String byteArrToHexString(byte[] bArr){
if (bArr == null)
return "";
diff --git a/src/main/java/konogonka/MainFx.java b/src/main/java/konogonka/MainFx.java
index f21fdbf..664c969 100644
--- a/src/main/java/konogonka/MainFx.java
+++ b/src/main/java/konogonka/MainFx.java
@@ -12,7 +12,7 @@ import java.util.Locale;
import java.util.ResourceBundle;
public class MainFx extends Application {
- public static final String appVersion = "v0.1-DEV";
+ public static final String appVersion = "v0.0-SNAPSHOT";
@Override
public void start(Stage primaryStage) throws Exception{
diff --git a/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java b/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java
new file mode 100644
index 0000000..95c1d19
--- /dev/null
+++ b/src/main/java/konogonka/Tools/NPDM/NPDMProvider.java
@@ -0,0 +1,77 @@
+package konogonka.Tools.NPDM;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import static konogonka.LoperConverter.*;
+
+public class NPDMProvider {
+
+ private String magicNum;
+ private byte[] reserved1;
+ private byte MMUFlags;
+ private byte reserved2;
+ private byte mainThreadPrio;
+ private byte mainThreadCoreNum;
+ private byte[] reserved3;
+ private int personalMmHeapSize; // safe-to-store
+ private int version; // safe?
+ private long mainThreadStackSize; // TODO: check if safe
+ 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)
+
+ 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
+ throw new Exception("NPDMProvider: File is too small.");
+ RandomAccessFile raf = new RandomAccessFile(file, "r");
+ raf.seek(offset);
+ // Get META
+ byte[] metaBuf = new byte[0x80];
+ if (raf.read(metaBuf) != 0x80)
+ throw new Exception("NPDMProvider: Failed to read 'META'");
+ magicNum = new String(metaBuf, 0, 4, StandardCharsets.UTF_8);
+ reserved1 = Arrays.copyOfRange(metaBuf, 0x4, 0xC);
+ MMUFlags = metaBuf[0xC];
+ reserved2 = metaBuf[0xD];
+ mainThreadPrio = metaBuf[0xE];
+ mainThreadCoreNum = metaBuf[0xF];
+ reserved3 = Arrays.copyOfRange(metaBuf, 0x10, 0x14);
+ personalMmHeapSize = getLEint(metaBuf, 0x14);
+ version = getLEint(metaBuf, 0x18);
+ mainThreadStackSize = getLElongOfInt(metaBuf, 0x1C);
+ 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);
+ }
+
+ public String getMagicNum() { return magicNum; }
+ public byte[] getReserved1() { return reserved1; }
+ public byte getMMUFlags() { return MMUFlags; }
+ public byte getReserved2() { return reserved2; }
+ public byte getMainThreadPrio() { return mainThreadPrio; }
+ public byte getMainThreadCoreNum() { return mainThreadCoreNum; }
+ public byte[] getReserved3() { return reserved3; }
+ public int getPersonalMmHeapSize() { return personalMmHeapSize; }
+ public int getVersion() { return version; }
+ public long getMainThreadStackSize() { return mainThreadStackSize; }
+ 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; }
+}
diff --git a/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java b/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java
index e025ec3..e50294b 100644
--- a/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java
+++ b/src/main/java/konogonka/Tools/PFS0/PFS0Provider.java
@@ -1,8 +1,5 @@
package konogonka.Tools.PFS0;
-import konogonka.ModelControllers.EMsgType;
-import konogonka.RainbowHexDump;
-
import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.Arrays;
diff --git a/src/main/java/konogonka/Workers/AnalyzerNCA.java b/src/main/java/konogonka/Workers/AnalyzerNCA.java
index 5b02985..3171f85 100644
--- a/src/main/java/konogonka/Workers/AnalyzerNCA.java
+++ b/src/main/java/konogonka/Workers/AnalyzerNCA.java
@@ -3,7 +3,6 @@ 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;
diff --git a/src/main/java/konogonka/Workers/AnalyzerNPDM.java b/src/main/java/konogonka/Workers/AnalyzerNPDM.java
new file mode 100644
index 0000000..4745e6e
--- /dev/null
+++ b/src/main/java/konogonka/Workers/AnalyzerNPDM.java
@@ -0,0 +1,46 @@
+package konogonka.Workers;
+
+import javafx.concurrent.Task;
+import konogonka.ModelControllers.EMsgType;
+import konogonka.ModelControllers.LogPrinter;
+import konogonka.Tools.NPDM.NPDMProvider;
+
+import java.io.File;
+
+public class AnalyzerNPDM extends Task {
+
+ private File file;
+ private long offset;
+ private LogPrinter logPrinter;
+
+ public AnalyzerNPDM(File file){
+ this(file, 0);
+ }
+
+ public AnalyzerNPDM(File file, long offset){
+ this.file = file;
+ this.offset = offset;
+ this.logPrinter = new LogPrinter();
+ }
+
+ @Override
+ protected NPDMProvider call() {
+ logPrinter.print("\tStart chain: NPDM", EMsgType.INFO);
+ try{
+ return new NPDMProvider(file, offset);
+ }
+ catch (Exception e){
+ logPrinter.print("\tException: "+e.getMessage(), EMsgType.FAIL);
+ return null;
+ }
+ finally {
+ close();
+ }
+ }
+
+ private void close(){
+ logPrinter.print("\tEnd chain: NPDM", EMsgType.INFO);
+ logPrinter.close();
+ }
+}
+
diff --git a/src/main/resources/FXML/NPDM/NPDMTab.fxml b/src/main/resources/FXML/NPDM/NPDMTab.fxml
new file mode 100644
index 0000000..75a8095
--- /dev/null
+++ b/src/main/resources/FXML/NPDM/NPDMTab.fxml
@@ -0,0 +1,442 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/src/main/resources/FXML/Settings/SettingsLayout.fxml b/src/main/resources/FXML/Settings/SettingsLayout.fxml
index e870201..7ade0e6 100644
--- a/src/main/resources/FXML/Settings/SettingsLayout.fxml
+++ b/src/main/resources/FXML/Settings/SettingsLayout.fxml
@@ -14,7 +14,7 @@
-
+
@@ -101,7 +101,7 @@
-
+
diff --git a/src/main/resources/FXML/landingPage.fxml b/src/main/resources/FXML/landingPage.fxml
index 906ae12..47e5bac 100644
--- a/src/main/resources/FXML/landingPage.fxml
+++ b/src/main/resources/FXML/landingPage.fxml
@@ -87,6 +87,14 @@
+
+
+
+
+
+
+
+