Hide 'extended network settings' instead of setting it disabled
All checks were successful
continuous-integration/drone/push Build is passing

This commit is contained in:
Dmitry Isaenko 2023-02-06 02:00:05 +03:00
parent c84f70ec10
commit 83695511d3
14 changed files with 557 additions and 134 deletions

38
pom.xml
View file

@ -8,13 +8,13 @@
<name>NS-USBloader</name>
<artifactId>ns-usbloader</artifactId>
<version>6.2</version>
<version>7.0</version>
<url>https://redrise.ru</url>
<description>
NS multi tool
</description>
<inceptionYear>2019</inceptionYear>
<inceptionYear>2019.0.2.1</inceptionYear>
<organization>
<name>Dmitry Isaenko</name>
<url>https://developersu.blogspot.com/</url>
@ -61,28 +61,28 @@
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>linux</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>linux</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>linux</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>linux</classifier>
<scope>compile</scope>
</dependency>
@ -90,28 +90,28 @@
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>win</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>win</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>win</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>win</classifier>
<scope>compile</scope>
</dependency>
@ -119,28 +119,28 @@
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac</classifier>
<scope>compile</scope>
</dependency>
@ -148,28 +148,28 @@
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-controls</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac-aarch64</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-media</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac-aarch64</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-fxml</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac-aarch64</classifier>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.openjfx</groupId>
<artifactId>javafx-graphics</artifactId>
<version>19</version>
<version>19.0.2.1</version>
<classifier>mac-aarch64</classifier>
<scope>compile</scope>
</dependency>
@ -309,7 +309,7 @@
<fileVersion>1.0.0.0</fileVersion>
<txtFileVersion>${project.version}</txtFileVersion>
<fileDescription>NS multi tool</fileDescription>
<copyright>GNU General Public License v3, 2019 ${project.organization.name}, Russia.</copyright>
<copyright>GNU General Public License v3, 2019.0.2.1 ${project.organization.name}, Russia.</copyright>
<productVersion>1.0.0.0</productVersion>
<txtProductVersion>${project.version}</txtProductVersion>
<companyName>${project.organization.name}</companyName>

View file

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

View file

@ -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());

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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");
}
}

View file

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

View file

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

View file

@ -76,7 +76,7 @@ public class FsPatch {
findAllOffsets();
mkDirs();
writeFile();
new IniMaker(logPrinter,
new FsIniMaker(logPrinter,
saveToLocation,
_textSection,
wizard.getOffset1(),

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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();
}
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
---
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);
}
}

View file

@ -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 <https://www.gnu.org/licenses/>.
*/
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<String, File> 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<Integer> 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;
}
}

View file

@ -48,7 +48,7 @@
<Label minHeight="-Infinity" minWidth="-Infinity" text="%tabPatches_Lbl_Firmware" wrapText="true" />
<Label fx:id="shortNameFirmwareLbl" textOverrun="LEADING_WORD_ELLIPSIS" />
<Pane HBox.hgrow="ALWAYS" />
<Button fx:id="selFwFolderBtn" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectFirmware" styleClass="buttonSelect" text="%tabSplMrg_Btn_SelectFolder" wrapText="true">
<Button minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectFirmware" styleClass="buttonSelect" text="%tabSplMrg_Btn_SelectFolder" wrapText="true">
<graphic>
<SVGPath content="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" fill="#289de8" />
</graphic>
@ -60,6 +60,23 @@
<Font name="System Italic" size="13.0" />
</font>
</Label>
<HBox alignment="CENTER_LEFT" spacing="5.0">
<children>
<Label minHeight="-Infinity" minWidth="-Infinity" text="%tabPatches_Lbl_Atmo" wrapText="true" />
<Label fx:id="shortNameAtmoLbl" textOverrun="LEADING_WORD_ELLIPSIS" />
<Pane HBox.hgrow="ALWAYS" />
<Button minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectAtmosphereFolder" styleClass="buttonSelect" text="%tabSplMrg_Btn_SelectFolder" wrapText="true">
<graphic>
<SVGPath content="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" fill="#289de8" />
</graphic>
</Button>
</children>
</HBox>
<Label fx:id="locationAtmosphereLbl" disable="true" textOverrun="LEADING_WORD_ELLIPSIS">
<font>
<Font name="System Italic" size="13.0" />
</font>
</Label>
</children>
</VBox>
<Separator prefWidth="200.0" />
@ -70,7 +87,7 @@
<Label minHeight="-Infinity" minWidth="-Infinity" text="%tabPatches_Lbl_Keys" wrapText="true" />
<Label fx:id="shortNameKeysLbl" />
<Pane HBox.hgrow="ALWAYS" />
<Button fx:id="selProdKeysBtn" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectProdKeys" styleClass="buttonSelect" text="%btn_Select">
<Button minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectProdKeys" styleClass="buttonSelect" text="%btn_Select">
<graphic>
<SVGPath content="M22,18V22H18V19H15V16H12L9.74,13.74C9.19,13.91 8.61,14 8,14A6,6 0 0,1 2,8A6,6 0 0,1 8,2A6,6 0 0,1 14,8C14,8.61 13.91,9.19 13.74,9.74L22,18M7,5A2,2 0 0,0 5,7A2,2 0 0,0 7,9A2,2 0 0,0 9,7A2,2 0 0,0 7,5Z" fill="#289de8" />
</graphic>
@ -90,7 +107,7 @@
<Label minHeight="-Infinity" minWidth="-Infinity" text="%tabSplMrg_Lbl_SaveToLocation" wrapText="true" />
<Label fx:id="saveToLbl" />
<Pane HBox.hgrow="ALWAYS" />
<Button fx:id="selProdKeysBtn1" minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectSaveTo" styleClass="buttonSelect" text="%btn_Select">
<Button minHeight="-Infinity" minWidth="-Infinity" mnemonicParsing="false" onAction="#selectSaveTo" styleClass="buttonSelect" text="%btn_Select">
<graphic>
<SVGPath content="M10,4H4C2.89,4 2,4.89 2,6V18A2,2 0 0,0 4,20H20A2,2 0 0,0 22,18V8C22,6.89 21.1,6 20,6H12L10,4Z" fill="#289de8" />
</graphic>
@ -114,6 +131,7 @@
<children>
<Button fx:id="makeEsBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeEs" />
<Button fx:id="makeFsBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeFs" />
<Button fx:id="makeLoaderBtn" contentDisplay="TOP" mnemonicParsing="false" styleClass="buttonUp" text="%tabPatches_Btn_MakeAtmo" />
</children>
</HBox>
<padding>

View file

@ -87,6 +87,7 @@ tabPatches_Lbl_Title=Patches
tabPatches_Lbl_Keys=Keys:
tabPatches_Btn_MakeEs=Make ES
tabPatches_Btn_MakeFs=Make FS
tabPatches_Btn_MakeAtmo=Make Atmo
tabPatches_Btn_MakeAtmo=Make Loader (Atmosphere)
tabPatches_Btn_MakeAll=Make all
tabPatches_ServiceWindowMessage=Both firmware and keys should be set to generate patches. Otherwise, it's not clear what to patch.
tabPatches_ServiceWindowMessageEsFs=Both firmware and keys should be set to generate patches. Otherwise, it's not clear what to patch.
tabPatches_ServiceWindowMessageLoader=Atmosphere folder should be defined to generate 'Loader' patch.

View file

@ -80,13 +80,14 @@ tabRcm_Lbl_FuseeGelee=Fus\u00E9e Gel\u00E9e RCM
tabPatches_Btn_asZipFile=\u0432 \u0432\u0438\u0434\u0435 ZIP
tabPatches_Btn_fromFolder=\u0418\u0437 \u043F\u0430\u043F\u043A\u0438
tabPatches_Btn_MakeAll=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0432\u0441\u0451
tabPatches_Btn_MakeAtmo=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F Atmo
tabPatches_Btn_MakeAtmo=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F Loader (Atmosphere)
tabPatches_Btn_MakeEs=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F ES
tabPatches_Btn_MakeFs=\u0421\u0433\u0435\u043D\u0435\u0440\u0438\u0440\u043E\u0432\u0430\u0442\u044C \u0434\u043B\u044F FS
tabPatches_Lbl_Atmo=Atmosphere:
tabPatches_Lbl_Firmware=\u041F\u0440\u043E\u0448\u0438\u0432\u043A\u0430:
tabPatches_Lbl_Keys=\u041A\u043B\u044E\u0447\u0438
tabPatches_Lbl_Title=\u041F\u0430\u0442\u0447\u0438
tabPatches_ServiceWindowMessage=\u0414\u043B\u044F \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043F\u0430\u0442\u0447\u0435\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043A\u0430\u043A \u043F\u0443\u0442\u044C \u043A \u043F\u0440\u043E\u0448\u0438\u0432\u043A\u0435, \u0442\u0430\u043A \u0438 \u043F\u0443\u0442\u044C \u043A \u0444\u0430\u0439\u043B\u0443 \u043A\u043B\u044E\u0447\u0435\u0439. \u0418\u043D\u0430\u0447\u0435 \u043D\u0435 \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u0447\u0442\u043E \u0436\u0435 \u043F\u0430\u0442\u0447\u0438\u0442\u044C.
tabPatches_ServiceWindowMessageEsFs=\u0414\u043B\u044F \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043F\u0430\u0442\u0447\u0435\u0439 \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043A\u0430\u043A \u043F\u0443\u0442\u044C \u043A \u043F\u0440\u043E\u0448\u0438\u0432\u043A\u0435, \u0442\u0430\u043A \u0438 \u043F\u0443\u0442\u044C \u043A \u0444\u0430\u0439\u043B\u0443 \u043A\u043B\u044E\u0447\u0435\u0439. \u0418\u043D\u0430\u0447\u0435 \u043D\u0435 \u043F\u043E\u043D\u044F\u0442\u043D\u043E \u0447\u0442\u043E \u0436\u0435 \u043F\u0430\u0442\u0447\u0438\u0442\u044C.
tabPatches_ServiceWindowMessageLoader=\u0414\u043B\u044F \u0441\u043E\u0437\u0434\u0430\u043D\u0438\u044F \u043F\u0430\u0442\u0447\u0430 \u00ABLoader\u00BB \u043D\u0435\u043E\u0431\u0445\u043E\u0434\u0438\u043C\u043E \u0443\u043A\u0430\u0437\u0430\u0442\u044C \u043F\u0443\u0442\u044C \u043A Atmosphere.

View file

@ -80,12 +80,13 @@ tabRcm_Lbl_FuseeGelee=Fus\u00E9e Gel\u00E9e RCM
tabPatches_Btn_asZipFile=\u044F\u043A ZIP
tabPatches_Btn_fromFolder=\u0417 \u0434\u0438\u0440\u0435\u043A\u0442\u043E\u0440\u0456\u0457
tabPatches_Btn_MakeAll=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0432\u0441\u0456
tabPatches_Btn_MakeAtmo=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0434\u043B\u044F Atmo
tabPatches_Btn_MakeAtmo=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0434\u043B\u044F Loader (Atmosphere)
tabPatches_Btn_MakeEs=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0434\u043B\u044F ES
tabPatches_Btn_MakeFs=\u0421\u0442\u0432\u043E\u0440\u0438\u0442\u0438 \u0434\u043B\u044F FS
tabPatches_Lbl_Atmo=Atmosphere:
tabPatches_Lbl_Firmware=\u041F\u0440\u043E\u0448\u0438\u0432\u043A\u0430:
tabPatches_Lbl_Keys=\u041A\u043B\u044E\u0447\u0456
tabPatches_Lbl_Title=\u041F\u0430\u0442\u0447\u0438
tabPatches_ServiceWindowMessage=\u0414\u043B\u044F \u0441\u0442\u0432\u043E\u0440\u0435\u043D\u043D\u044F \u043F\u0430\u0442\u0447\u0456\u0432 \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u043E \u0432\u043A\u0430\u0437\u0430\u0442\u0438 \u044F\u043A \u0448\u043B\u044F\u0445 \u0434\u043E \u043F\u0440\u043E\u0448\u0438\u0432\u043A\u0438, \u0442\u0430\u043A \u0456 \u0434\u043E \u0444\u0430\u0439\u043B\u0443 \u043A\u043B\u044E\u0447\u0456\u0432. \u0411\u043E \u0456\u043D\u0430\u043A\u0448\u0435 \u043D\u0435 \u0437\u0440\u043E\u0437\u0443\u043C\u0456\u043B\u043E \u0449\u043E \u0436 \u0442\u0440\u0435\u0431\u0430 \u043F\u0430\u0442\u0447\u0438\u0442\u0438.
tabPatches_ServiceWindowMessageEsFs=\u0414\u043B\u044F \u0441\u0442\u0432\u043E\u0440\u0435\u043D\u043D\u044F \u043F\u0430\u0442\u0447\u0456\u0432 \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u043E \u0432\u043A\u0430\u0437\u0430\u0442\u0438 \u044F\u043A \u0448\u043B\u044F\u0445 \u0434\u043E \u043F\u0440\u043E\u0448\u0438\u0432\u043A\u0438, \u0442\u0430\u043A \u0456 \u0434\u043E \u0444\u0430\u0439\u043B\u0443 \u043A\u043B\u044E\u0447\u0456\u0432. \u0411\u043E \u0456\u043D\u0430\u043A\u0448\u0435 \u043D\u0435 \u0437\u0440\u043E\u0437\u0443\u043C\u0456\u043B\u043E \u0449\u043E \u0436 \u0442\u0440\u0435\u0431\u0430 \u043F\u0430\u0442\u0447\u0438\u0442\u0438.
tabPatches_ServiceWindowMessageLoader=\u0414\u043B\u044F \u0433\u0435\u043D\u0435\u0440\u0430\u0446\u0456\u0457 "Loader"-\u043F\u0430\u0442\u0447\u0443 \u043D\u0435\u043E\u0431\u0445\u0456\u0434\u043D\u043E \u0432\u043A\u0430\u0437\u0430\u0442\u0438 \u0448\u043B\u044F\u0445 \u0434\u043E Atmosphere.