diff --git a/pom.xml b/pom.xml
index 048eb11..daebc2b 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,13 +8,13 @@
NS-USBloader
ns-usbloader
- 6.2
+ 7.0
https://redrise.ru
NS multi tool
- 2019
+ 2019.0.2.1
Dmitry Isaenko
https://developersu.blogspot.com/
@@ -61,28 +61,28 @@
org.openjfx
javafx-controls
- 19
+ 19.0.2.1
linux
compile
org.openjfx
javafx-media
- 19
+ 19.0.2.1
linux
compile
org.openjfx
javafx-fxml
- 19
+ 19.0.2.1
linux
compile
org.openjfx
javafx-graphics
- 19
+ 19.0.2.1
linux
compile
@@ -90,28 +90,28 @@
org.openjfx
javafx-controls
- 19
+ 19.0.2.1
win
compile
org.openjfx
javafx-media
- 19
+ 19.0.2.1
win
compile
org.openjfx
javafx-fxml
- 19
+ 19.0.2.1
win
compile
org.openjfx
javafx-graphics
- 19
+ 19.0.2.1
win
compile
@@ -119,28 +119,28 @@
org.openjfx
javafx-controls
- 19
+ 19.0.2.1
mac
compile
org.openjfx
javafx-media
- 19
+ 19.0.2.1
mac
compile
org.openjfx
javafx-fxml
- 19
+ 19.0.2.1
mac
compile
org.openjfx
javafx-graphics
- 19
+ 19.0.2.1
mac
compile
@@ -148,28 +148,28 @@
org.openjfx
javafx-controls
- 19
+ 19.0.2.1
mac-aarch64
compile
org.openjfx
javafx-media
- 19
+ 19.0.2.1
mac-aarch64
compile
org.openjfx
javafx-fxml
- 19
+ 19.0.2.1
mac-aarch64
compile
org.openjfx
javafx-graphics
- 19
+ 19.0.2.1
mac-aarch64
compile
@@ -309,7 +309,7 @@
1.0.0.0
${project.version}
NS multi tool
- GNU General Public License v3, 2019 ${project.organization.name}, Russia.
+ GNU General Public License v3, 2019.0.2.1 ${project.organization.name}, Russia.
1.0.0.0
${project.version}
${project.organization.name}
diff --git a/src/main/java/nsusbloader/Controllers/PatchesController.java b/src/main/java/nsusbloader/Controllers/PatchesController.java
index 7b0261b..5aeb672 100644
--- a/src/main/java/nsusbloader/Controllers/PatchesController.java
+++ b/src/main/java/nsusbloader/Controllers/PatchesController.java
@@ -41,15 +41,17 @@ import nsusbloader.NSLDataTypes.EModule;
import nsusbloader.ServiceWindow;
import nsusbloader.Utilities.patches.es.EsPatchMaker;
import nsusbloader.Utilities.patches.fs.FsPatchMaker;
+import nsusbloader.Utilities.patches.loader.LoaderPatchMaker;
// TODO: CLI SUPPORT
public class PatchesController implements Initializable {
@FXML
private VBox patchesToolPane;
@FXML
- private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn, makeFsBtn;
+ private Button makeEsBtn, makeFsBtn, makeLoaderBtn;
@FXML
- private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl;
+ private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl,
+ locationAtmosphereLbl, shortNameAtmoLbl;
private Thread workThread;
private String previouslyOpenedPath;
@@ -72,13 +74,14 @@ public class PatchesController implements Initializable {
locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, "")));
- convertRegionEs = new Region();
- convertRegionEs.getStyleClass().add("regionCake");
+ locationAtmosphereLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
+ shortNameAtmoLbl.setText(updatedText.replaceAll(myRegexp, "")));
+
+ convertRegionEs = createCakeRegion();
makeEsBtn.setGraphic(convertRegionEs);
- Region cakeRegionFs = new Region();
- cakeRegionFs.getStyleClass().add("regionCake");
- makeFsBtn.setGraphic(cakeRegionFs);
+ makeFsBtn.setGraphic(createCakeRegion());
+ makeLoaderBtn.setGraphic(createCakeRegion());
AppPreferences preferences = AppPreferences.getInstance();
String keysLocation = preferences.getKeysLocation();
@@ -89,11 +92,23 @@ public class PatchesController implements Initializable {
}
saveToLbl.setText(preferences.getPatchesSaveToLocation());
- makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
+ makeEsBtn.disableProperty().bind(Bindings.or(
+ Bindings.isEmpty(locationFirmwareLbl.textProperty()),
+ Bindings.isEmpty(locationKeysLbl.textProperty())));
makeEsBtn.setOnAction(actionEvent -> makeEs());
- makeFsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
+ makeFsBtn.disableProperty().bind(Bindings.or(
+ Bindings.isEmpty(locationFirmwareLbl.textProperty()),
+ Bindings.isEmpty(locationKeysLbl.textProperty())));
makeFsBtn.setOnAction(actionEvent -> makeFs());
+
+ makeLoaderBtn.disableProperty().bind(Bindings.isEmpty(locationAtmosphereLbl.textProperty()));
+ makeLoaderBtn.setOnAction(actionEvent -> makeLoader());
+ }
+ private Region createCakeRegion(){
+ Region cakeRegion = new Region();
+ cakeRegion.getStyleClass().add("regionCake");
+ return cakeRegion;
}
/**
@@ -135,6 +150,18 @@ public class PatchesController implements Initializable {
if (firmware == null)
return;
locationFirmwareLbl.setText(firmware.getAbsolutePath());
+ previouslyOpenedPath = firmware.getParent();
+ }
+ @FXML
+ private void selectAtmosphereFolder(){
+ DirectoryChooser directoryChooser = new DirectoryChooser();
+ directoryChooser.setTitle(resourceBundle.getString("tabPatches_Lbl_Atmo"));
+ directoryChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
+ File firmware = directoryChooser.showDialog(patchesToolPane.getScene().getWindow());
+ if (firmware == null)
+ return;
+ locationAtmosphereLbl.setText(firmware.getAbsolutePath());
+ previouslyOpenedPath = firmware.getParent();
}
@FXML
private void selectSaveTo(){
@@ -153,16 +180,17 @@ public class PatchesController implements Initializable {
fileChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("keys", "*.dat", "*.keys"));
File keys = fileChooser.showOpenDialog(patchesToolPane.getScene().getWindow());
+ if (keys == null || ! keys.exists())
+ return;
- if (keys != null && keys.exists()) {
- locationKeysLbl.setText(keys.getAbsolutePath());
- }
+ locationKeysLbl.setText(keys.getAbsolutePath());
+ previouslyOpenedPath = keys.getParent();
}
private void makeEs(){
if (locationFirmwareLbl.getText().isEmpty() || locationKeysLbl.getText().isEmpty()){
ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
- resourceBundle.getString("tabPatches_ServiceWindowMessage"));
+ resourceBundle.getString("tabPatches_ServiceWindowMessageEsFs"));
return;
}
@@ -186,7 +214,7 @@ public class PatchesController implements Initializable {
private void makeFs(){
if (locationFirmwareLbl.getText().isEmpty() || locationKeysLbl.getText().isEmpty()){
ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
- resourceBundle.getString("tabPatches_ServiceWindowMessage"));
+ resourceBundle.getString("tabPatches_ServiceWindowMessageEsFs"));
return;
}
@@ -207,6 +235,29 @@ public class PatchesController implements Initializable {
workThread.setDaemon(true);
workThread.start();
}
+ private void makeLoader(){
+ if (locationAtmosphereLbl.getText().isEmpty()){
+ ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
+ resourceBundle.getString("tabPatches_ServiceWindowMessageLoader"));
+ return;
+ }
+
+ if (workThread != null && workThread.isAlive())
+ return;
+ statusLbl.setText("");
+
+ if (MediatorControl.getInstance().getTransferActive()) {
+ ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
+ resourceBundle.getString("windowBodyPleaseStopOtherProcessFirst"));
+ return;
+ }
+
+ LoaderPatchMaker loaderPatchMaker = new LoaderPatchMaker(locationAtmosphereLbl.getText(), saveToLbl.getText());
+ workThread = new Thread(loaderPatchMaker);
+
+ workThread.setDaemon(true);
+ workThread.start();
+ }
private void interruptProcessOfPatchMaking(){
if (workThread == null || ! workThread.isAlive())
return;
@@ -222,6 +273,7 @@ public class PatchesController implements Initializable {
convertRegionEs.getStyleClass().clear();
makeFsBtn.setVisible(! isActive);
+ makeLoaderBtn.setVisible(! isActive);
if (isActive) {
MediatorControl.getInstance().getContoller().logArea.clear();
@@ -231,15 +283,14 @@ public class PatchesController implements Initializable {
makeEsBtn.setText(resourceBundle.getString("btn_Stop"));
makeEsBtn.getStyleClass().remove("buttonUp");
makeEsBtn.getStyleClass().add("buttonStop");
+ return;
}
- else {
- convertRegionEs.getStyleClass().add("regionCake");
+ convertRegionEs.getStyleClass().add("regionCake");
- makeEsBtn.setOnAction(actionEvent -> makeEs());
- makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
- makeEsBtn.getStyleClass().remove("buttonStop");
- makeEsBtn.getStyleClass().add("buttonUp");
- }
+ makeEsBtn.setOnAction(actionEvent -> makeEs());
+ makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
+ makeEsBtn.getStyleClass().remove("buttonStop");
+ makeEsBtn.getStyleClass().add("buttonUp");
}
public void setOneLineStatus(boolean statusSuccess){
diff --git a/src/main/java/nsusbloader/Controllers/SettingsBlockTinfoilController.java b/src/main/java/nsusbloader/Controllers/SettingsBlockTinfoilController.java
index 8c28be3..ec6c5f9 100644
--- a/src/main/java/nsusbloader/Controllers/SettingsBlockTinfoilController.java
+++ b/src/main/java/nsusbloader/Controllers/SettingsBlockTinfoilController.java
@@ -55,7 +55,7 @@ public class SettingsBlockTinfoilController implements Initializable {
final AppPreferences preferences = AppPreferences.getInstance();
- networkExpertSettingsVBox.disableProperty().bind(networkExpertModeCB.selectedProperty().not());
+ networkExpertSettingsVBox.visibleProperty().bind(networkExpertModeCB.selectedProperty());
pcIpTF.disableProperty().bind(autoDetectIpCB.selectedProperty());
pcPortTF.disableProperty().bind(randomlySelectPortCB.selectedProperty());
diff --git a/src/main/java/nsusbloader/RainbowHexDump.java b/src/main/java/nsusbloader/RainbowHexDump.java
deleted file mode 100644
index 109957d..0000000
--- a/src/main/java/nsusbloader/RainbowHexDump.java
+++ /dev/null
@@ -1,71 +0,0 @@
-/*
- Copyright 2019-2020 Dmitry Isaenko
-
- This file is part of NS-USBloader.
-
- NS-USBloader is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- NS-USBloader is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with NS-USBloader. If not, see .
-*/
-package nsusbloader;
-
-import java.nio.charset.StandardCharsets;
-
-/**
- * Debug tool like hexdump <3
- */
-public class RainbowHexDump {
- private static final String ANSI_RESET = "\u001B[0m";
- private static final String ANSI_BLACK = "\u001B[30m";
- private static final String ANSI_RED = "\u001B[31m";
- private static final String ANSI_GREEN = "\u001B[32m";
- private static final String ANSI_YELLOW = "\u001B[33m";
- private static final String ANSI_BLUE = "\u001B[34m";
- private static final String ANSI_PURPLE = "\u001B[35m";
- private static final String ANSI_CYAN = "\u001B[36m";
- private static final String ANSI_WHITE = "\u001B[37m";
-
- public static void hexDumpUTF8(byte[] byteArray){
- System.out.print(ANSI_BLUE);
- for (int i=0; i < byteArray.length; i++)
- System.out.printf("%02d-", i%100);
- System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET);
- for (byte b: byteArray)
- System.out.printf("%02x ", b);
- System.out.println();
- System.out.print("\t\t\t"
- + new String(byteArray, StandardCharsets.UTF_8)
- + "\n");
- }
-
- public static void hexDumpUTF8ForWin(byte[] byteArray){
- for (int i=0; i < byteArray.length; i++)
- System.out.printf("%02d-", i%100);
- System.out.println(">"+byteArray.length);
- for (byte b: byteArray)
- System.out.printf("%02x ", b);
- System.out.println();
- System.out.print(new String(byteArray, StandardCharsets.UTF_8)
- + "\n");
- }
-
- public static void hexDumpUTF16LE(byte[] byteArray){
- System.out.print(ANSI_BLUE);
- for (int i=0; i < byteArray.length; i++)
- System.out.printf("%02d-", i%100);
- System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET);
- for (byte b: byteArray)
- System.out.printf("%02x ", b);
- System.out.print(new String(byteArray, StandardCharsets.UTF_16LE)
- + "\n");
- }
-}
diff --git a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
index 622d27c..0109258 100644
--- a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
+++ b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
@@ -111,7 +111,7 @@ public class BinToAsmPrinter {
return printImTooLazy("LDP", instructionExpression, offset);
}
- switch ((instructionExpression >> 23 & 0xff)){
+ switch ((instructionExpression >> 23 & 0x1ff)){
case 0xA5:
return printMOVSimplified(instructionExpression, offset);
case 0x22:
@@ -123,10 +123,10 @@ public class BinToAsmPrinter {
case 0xA2:
return printSUBSimplified(instructionExpression, offset);
case 0xE2:
- //case 0x1e2:
+ case 0x1e2:
return printCMPSimplified(instructionExpression, offset);
case 0x24:
- //case 0x124:
+ case 0x124:
return printANDSimplified(instructionExpression, offset);
}
@@ -142,6 +142,10 @@ public class BinToAsmPrinter {
return printTBZSimplified(instructionExpression, offset);
case 0x54:
return printBConditionalSimplified(instructionExpression, offset);
+ case 0xeb:
+ case 0x6b:
+ if ((instructionExpression & 0x1f) == 0b11111)
+ return printCMPShiftedRegisterSimplified(instructionExpression, offset);
}
switch (instructionExpression >> 26 & 0b111111) {
@@ -498,6 +502,37 @@ public class BinToAsmPrinter {
LSL);
}
+ private static String printCMPShiftedRegisterSimplified(int instructionExpression, int offset){
+ String sf = (instructionExpression >> 31 == 0) ? "W" : "X";
+ int Rn = instructionExpression >> 5 & 0x1F;
+ int Rm = instructionExpression >> 16 & 0x1F;
+ int imm6 = instructionExpression >> 10 & 0x3f;
+ int LSL = (instructionExpression >> 22 & 0b11);
+ String LSLStr;
+ switch (LSL){
+ case 0b00:
+ LSLStr = "LSL";
+ break;
+ case 0b01:
+ LSLStr = "LSR";
+ break;
+ case 0b10:
+ LSLStr = "ASR";
+ break;
+ case 0b11:
+ LSLStr = "RESERVED";
+ break;
+ default:
+ LSLStr = "?";
+ }
+
+ return String.format(
+ "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP (sr) " + ANSI_GREEN + sf + "%d," +
+ ANSI_BLUE + sf + "%d " + ANSI_BLUE + LSLStr + ANSI_PURPLE + " %d" + ANSI_RESET + "\n",
+ offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ Rn, Rm, imm6);
+ }
+
private static String printANDSimplified(int instructionExpression, int offset){
String sf = (instructionExpression >> 31 == 0) ? "W" : "X";
int Rn = instructionExpression & 0x1F;
diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/IniMaker.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsIniMaker.java
similarity index 94%
rename from src/main/java/nsusbloader/Utilities/patches/fs/IniMaker.java
rename to src/main/java/nsusbloader/Utilities/patches/fs/FsIniMaker.java
index ecf42fc..64551ef 100644
--- a/src/main/java/nsusbloader/Utilities/patches/fs/IniMaker.java
+++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsIniMaker.java
@@ -31,7 +31,7 @@ import java.nio.file.Paths;
import java.nio.file.StandardCopyOption;
import java.util.Arrays;
-public class IniMaker {
+public class FsIniMaker {
private static final String FILE_HEADER_TEXT = "# UTF-8\n" +
"# A KIP section is [kip1_name:sha256_hex_8bytes]\n" +
"# A patchset is .patch_name=kip_section_dec:offset_hex_0x:length_hex_0x:src_data_hex,dst_data_hex\n" +
@@ -48,14 +48,14 @@ public class IniMaker {
private String patchSet1;
private String patchSet2;
- IniMaker(ILogPrinter logPrinter,
- String saveToLocation,
- byte[] _textSection,
- int wizardOffset1,
- int wizardOffset2,
- byte[] sdkVersion,
- String patchName,
- boolean filesystemTypeFat32) throws Exception{
+ public FsIniMaker(ILogPrinter logPrinter,
+ String saveToLocation,
+ byte[] _textSection,
+ int wizardOffset1,
+ int wizardOffset2,
+ byte[] sdkVersion,
+ String patchName,
+ boolean filesystemTypeFat32) throws Exception{
this.logPrinter = logPrinter;
this.saveToLocation = saveToLocation;
this.offset1 = wizardOffset1 - 4;
diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
index 368ea0b..7849f50 100644
--- a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
+++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
@@ -76,7 +76,7 @@ public class FsPatch {
findAllOffsets();
mkDirs();
writeFile();
- new IniMaker(logPrinter,
+ new FsIniMaker(logPrinter,
saveToLocation,
_textSection,
wizard.getOffset1(),
diff --git a/src/main/java/nsusbloader/Utilities/patches/loader/LoaderIniMaker.java b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderIniMaker.java
new file mode 100644
index 0000000..d8ef04d
--- /dev/null
+++ b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderIniMaker.java
@@ -0,0 +1,115 @@
+/*
+ Copyright 2019-2023 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.Utilities.patches.loader;
+
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.Utilities.patches.MalformedIniFileException;
+
+import java.io.File;
+import java.io.RandomAccessFile;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.nio.file.StandardCopyOption;
+
+public class LoaderIniMaker {
+ private static final String FILE_HEADER_TEXT = "# UTF-8\n" +
+ "# A KIP section is [kip1_name:sha256_hex_8bytes]\n" +
+ "# A patchset is .patch_name=kip_section_dec:offset_hex_0x:length_hex_0x:src_data_hex,dst_data_hex\n" +
+ "# _dec: 1 char decimal | _hex_0x: max u32 prefixed with 0x | _hex: hex array.\n" +
+ "# Kip1 section decimals: TEXT: 0, RODATA: 1, DATA: 2.\n"; // Sending good vibes to Mr. ITotalJustice
+
+ private final ILogPrinter logPrinter;
+ private final String saveToLocation;
+ private final int offset;
+
+ private String sectionDeclaration;
+ private String patchSet;
+
+ LoaderIniMaker(ILogPrinter logPrinter,
+ String saveToLocation,
+ int foundOffset,
+ String patchName) throws Exception{
+ this.logPrinter = logPrinter;
+ this.saveToLocation = saveToLocation;
+ this.offset = foundOffset + 6;
+
+ mkDirs();
+ makeSectionDeclaration(patchName);
+ makePatchSet1();
+ writeFile();
+ }
+
+ private void mkDirs(){
+ File parentFolder = new File(saveToLocation + File.separator + "bootloader");
+ parentFolder.mkdirs();
+ }
+
+ private void makeSectionDeclaration(String patchName){
+ sectionDeclaration = "[Loader:"+patchName.substring(0, 16)+"]";
+ }
+
+ private void makePatchSet1(){
+ patchSet = String.format(".nosigchk=0:0x%02X:0x1:01,00", offset);
+ }
+
+ private void writeFile() throws Exception{
+ final String iniLocation = saveToLocation + File.separator + "bootloader" + File.separator + "patches.ini";
+ final Path iniLocationPath = Paths.get(iniLocation);
+
+ boolean iniNotExists = Files.notExists(iniLocationPath);
+
+ try (RandomAccessFile ini = new RandomAccessFile(iniLocation, "rw")){
+ if (iniNotExists)
+ ini.writeBytes(FILE_HEADER_TEXT);
+ else {
+ String line;
+ while ((line = ini.readLine()) != null){
+ if (! line.startsWith(sectionDeclaration))
+ continue;
+
+ String expression = ini.readLine();
+
+ if (expression == null || ! expression.startsWith(patchSet))
+ throw new MalformedIniFileException("Somewhere near "+ini.getFilePointer());
+
+ return; // Ini file already contains correct information regarding patch file we made.
+ }
+ }
+
+ ini.writeBytes("\n#Loader (Atmosphere)\n");
+ ini.writeBytes(sectionDeclaration);
+ ini.writeBytes("\n");
+
+ ini.writeBytes(patchSet);
+ ini.writeBytes("\n");
+ }
+ catch (MalformedIniFileException e){
+ e.printStackTrace();
+ logPrinter.print(
+ "Existing patches.ini file is malformed or contains incorrect (outdated) information regarding current patch.\n" +
+ "It's now saved at "+iniLocation+".OLD\n" +
+ "New patches.ini file created instead.", EMsgType.WARNING);
+ Files.move(iniLocationPath, Paths.get(iniLocation+".OLD"),
+ StandardCopyOption.ATOMIC_MOVE, StandardCopyOption.REPLACE_EXISTING);
+ writeFile();
+ }
+ }
+}
diff --git a/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatch.java b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatch.java
new file mode 100644
index 0000000..ad25afd
--- /dev/null
+++ b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatch.java
@@ -0,0 +1,147 @@
+/*
+ Copyright 2018-2023 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ ---
+ Based on https://github.com/mrdude2478/IPS_Patch_Creator patch script made by GBATemp member MrDude.
+ */
+package nsusbloader.Utilities.patches.loader;
+
+import libKonogonka.Converter;
+import libKonogonka.fs.other.System2.ini1.KIP1Provider;
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.Utilities.patches.BinToAsmPrinter;
+import nsusbloader.Utilities.patches.SimplyFind;
+
+import java.io.BufferedInputStream;
+import java.io.BufferedOutputStream;
+import java.io.File;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.charset.StandardCharsets;
+import java.nio.file.Files;
+import java.nio.file.Paths;
+import java.security.MessageDigest;
+import java.util.Arrays;
+
+public class LoaderPatch {
+ private static final byte[] HEADER = "PATCH".getBytes(StandardCharsets.US_ASCII);
+ private static final byte[] FOOTER = "EOF".getBytes(StandardCharsets.US_ASCII);
+
+ private static final String ATMOSPHERE_NEW_PATTERN = "01C0BE121F00016B";
+ //private static final String ATMOSPHERE_OLD_PATTERN = "003C00121F280071"; Must be patched using different (to current implementation) code
+
+ private final String saveToLocation;
+ private final ILogPrinter logPrinter;
+
+ private String patchName;
+ private byte[] _textSection;
+
+ private int offset;
+
+ LoaderPatch(KIP1Provider loaderProvider,
+ String saveToLocation,
+ ILogPrinter logPrinter) throws Exception{
+ this.saveToLocation = saveToLocation;
+ this.logPrinter = logPrinter;
+
+ getPatchName(loaderProvider);
+ getTextSection(loaderProvider);
+ findOffset();
+ mkDirs();
+ writeFile();
+ new LoaderIniMaker(logPrinter, saveToLocation, offset, patchName);
+ }
+ private void getPatchName(KIP1Provider kip1Provider) throws Exception{
+ int kip1EncryptedSize = (int) kip1Provider.getSize();
+ byte[] kip1EncryptedRaw = new byte[kip1EncryptedSize];
+
+ try (BufferedInputStream kip1ProviderStream = kip1Provider.getStreamProducer().produce()) {
+ if (kip1EncryptedSize != kip1ProviderStream.read(kip1EncryptedRaw))
+ throw new Exception("Unencrypted FS KIP1 read failure");
+ }
+
+ byte[] sha256ofKip1 = MessageDigest.getInstance("SHA-256").digest(kip1EncryptedRaw);
+ patchName = Converter.byteArrToHexStringAsLE(sha256ofKip1, true) + ".ips";
+ }
+ private void getTextSection(KIP1Provider kip1Provider) throws Exception{
+ _textSection = kip1Provider.getAsDecompressed().getTextRaw();
+ }
+ private void findOffset() throws Exception{
+ SimplyFind simplyFind = new SimplyFind(ATMOSPHERE_NEW_PATTERN, _textSection); // Atm 13+
+ if (simplyFind.getResults().size() == 0)
+ throw new Exception("Offset not found");
+
+ offset = simplyFind.getResults().get(0);
+
+ if (offset <= 0)
+ throw new Exception("Found offset is incorrect");
+
+ for (int i = 0; i < simplyFind.getResults().size(); i++) {
+ int offsetInternal = simplyFind.getResults().get(i) + 4;
+ logPrinter.print("Only first (#1) found record will be patched!", EMsgType.INFO);
+ logPrinter.print("Found #" + (i+1) +"\n"+
+ BinToAsmPrinter.printSimplified(Converter.getLEint(_textSection, offsetInternal), offsetInternal) +
+ BinToAsmPrinter.printSimplified(Converter.getLEint(_textSection, offsetInternal + 4), offsetInternal + 4) +
+ BinToAsmPrinter.printSimplified(Converter.getLEint(_textSection, offsetInternal + 8), offsetInternal + 8) +
+ BinToAsmPrinter.printSimplified(Converter.getLEint(_textSection, offsetInternal + 12), offsetInternal + 12),
+ EMsgType.NULL);
+ }
+ }
+ private void mkDirs(){
+ File parentFolder = new File(saveToLocation + File.separator +
+ "atmosphere" + File.separator + "kip_patches" + File.separator + "loader_patches");
+ parentFolder.mkdirs();
+ }
+
+ private void writeFile() throws Exception{
+ String patchFileLocation = saveToLocation + File.separator +
+ "atmosphere" + File.separator + "kip_patches" + File.separator + "fs_patches" + File.separator + patchName;
+
+ ByteBuffer handyFsPatch = ByteBuffer.allocate(0x100).order(ByteOrder.LITTLE_ENDIAN);
+ handyFsPatch.put(HEADER);
+ handyFsPatch.put(getPatch1(offset));
+ handyFsPatch.put(FOOTER);
+
+ byte[] fsPatch = new byte[handyFsPatch.position()];
+ handyFsPatch.rewind();
+ handyFsPatch.get(fsPatch);
+
+ try (BufferedOutputStream stream = new BufferedOutputStream(
+ Files.newOutputStream(Paths.get(patchFileLocation)))){
+ stream.write(fsPatch);
+ }
+ logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS);
+ }
+
+ private byte[] getPatch1(int offset) throws Exception{
+ int requiredInstructionOffsetInternal = offset + 6;
+ int requiredInstructionOffsetReal = requiredInstructionOffsetInternal + 0x100;
+ final byte[] patch = new byte[]{0x00, 0x01, 0x00};
+
+ int instructionPatched = Converter.getLEint(_textSection, offset + 4) & 0xff00ffff;
+
+ logPrinter.print("Patch will be applied", EMsgType.PASS);
+ logPrinter.print(BinToAsmPrinter.printSimplified(instructionPatched, offset+4), EMsgType.NULL);
+
+ ByteBuffer prePatch = ByteBuffer.allocate(7).order(ByteOrder.BIG_ENDIAN)
+ .putInt(requiredInstructionOffsetReal)
+ .put(patch);
+
+ return Arrays.copyOfRange(prePatch.array(), 1, 7);
+ }
+}
diff --git a/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatchMaker.java b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatchMaker.java
new file mode 100644
index 0000000..ce575f5
--- /dev/null
+++ b/src/main/java/nsusbloader/Utilities/patches/loader/LoaderPatchMaker.java
@@ -0,0 +1,125 @@
+/*
+ Copyright 2018-2022 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.Utilities.patches.loader;
+
+import libKonogonka.Converter;
+import libKonogonka.fs.other.System2.ini1.KIP1Provider;
+import nsusbloader.ModelControllers.CancellableRunnable;
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.ModelControllers.Log;
+import nsusbloader.NSLDataTypes.EModule;
+import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.Utilities.patches.SimplyFind;
+
+import java.io.BufferedInputStream;
+import java.io.File;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.*;
+
+public class LoaderPatchMaker extends CancellableRunnable {
+
+ private final ILogPrinter logPrinter;
+ private final String atmosphereLocation;
+ private final String saveTo;
+
+ private String package3Location;
+ private KIP1Provider loaderProvider;
+
+ private boolean oneLinerStatus = false;
+
+ public LoaderPatchMaker(String atmosphereLocation, String saveTo){
+ this.logPrinter = Log.getPrinter(EModule.PATCHES);
+ /*
+ this.logPrinter = new ILogPrinter() {
+ public void print(String message, EMsgType type) throws InterruptedException {}
+ public void updateProgress(Double value) throws InterruptedException {}
+ public void update(HashMap nspMap, EFileStatus status) {}
+ public void update(File file, EFileStatus status) {}
+ public void updateOneLinerStatus(boolean status) {}
+ public void close() {}
+ };
+ //*/
+ this.atmosphereLocation = atmosphereLocation;
+ this.saveTo = saveTo;
+ }
+
+ @Override
+ public void run() {
+ try {
+ logPrinter.print("..:: Make Loader Patches ::..", EMsgType.INFO);
+ checkPackage3();
+ createLoaderKip1Provider();
+ makePatches();
+ }
+ catch (Exception e){
+ e.printStackTrace();
+ try{
+ logPrinter.print(e.getMessage(), EMsgType.FAIL);
+ } catch (Exception ignore){}
+ }
+ finally {
+ logPrinter.updateOneLinerStatus(oneLinerStatus);
+ logPrinter.close();
+ }
+ }
+ private void checkPackage3() throws Exception{
+ logPrinter.print("Looking at Atmosphere", EMsgType.INFO);
+ if (Files.notExists(Paths.get(atmosphereLocation)))
+ throw new Exception("Atmosphere directory does not exist at " + atmosphereLocation);
+
+ package3Location = atmosphereLocation +File.separator+"package3";
+ if (Files.exists(Paths.get(package3Location)))
+ return;
+
+ package3Location = atmosphereLocation +File.separator+"fusee-secondary.bin";
+ if (Files.notExists(Paths.get(package3Location)))
+ throw new Exception("package3 / fusee-secondary.bin file not found at " + atmosphereLocation);
+ }
+
+ private void createLoaderKip1Provider() throws Exception{
+ Path package3Path = Paths.get(package3Location);
+
+ try (BufferedInputStream stream = new BufferedInputStream(Files.newInputStream(package3Path))) {
+ byte[] data = new byte[0x400];
+ if (0x400 != stream.read(data))
+ throw new Exception("Failed to read first 0x400 bytes of package3 / fusee-secondary file.");
+
+ SimplyFind simplyFind = new SimplyFind(".6f61646572", data); // eq. '.oader'
+ List results = simplyFind.getResults();
+ if (results.size() == 0)
+ throw new Exception("Failed to find 'Loader' offset at package3 / fusee-secondary file.");
+
+ int offset = results.get(0);
+ int kip1Offset = Converter.getLEint(data, offset - 0x10);
+ int kip1Size = Converter.getLEint(data, offset - 0xC);
+
+ loaderProvider = new KIP1Provider(package3Location, kip1Offset);
+
+ if (kip1Size != loaderProvider.getSize())
+ throw new Exception("Incorrect calculations for KIP1. PK31 value: "+kip1Size+"KIP1Provider value: "+loaderProvider.getSize());
+ logPrinter.print("Loader KIP1 found", EMsgType.PASS);
+ }
+ }
+ private void makePatches() throws Exception{
+ new LoaderPatch(loaderProvider, saveTo, logPrinter);
+ oneLinerStatus = true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/PatchesTab.fxml b/src/main/resources/PatchesTab.fxml
index c7d0422..ee72f6c 100644
--- a/src/main/resources/PatchesTab.fxml
+++ b/src/main/resources/PatchesTab.fxml
@@ -48,7 +48,7 @@
-