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 @@