mirror of
https://github.com/developersu/ns-usbloader.git
synced 2025-05-15 15:44:55 -04:00
Correct readme, update few icons, add freedesktop entry and svg icon. Could be useful for packagers.
This commit is contained in:
parent
8b23b8967b
commit
55df39923f
32 changed files with 2433 additions and 205 deletions
|
@ -88,6 +88,8 @@ public class AppPreferences {
|
|||
public String getHostIp(){ return preferences.get("HOSTIP", "0.0.0.0").replaceAll("(\\s)|(\t)", "");} // who the hell said 'paranoid'?
|
||||
public void setHostIp(String ip){preferences.put("HOSTIP", ip);}
|
||||
|
||||
public void give(){preferences.putBoolean("man", true);}
|
||||
public boolean take(){return preferences.getBoolean("man", false);}
|
||||
public String getHostPort(){
|
||||
String value = preferences.get("HOSTPORT", "6042");
|
||||
if (!value.matches("^[0-9]{1,5}$"))
|
||||
|
@ -138,4 +140,10 @@ public class AppPreferences {
|
|||
|
||||
public String getLastOpenedTab(){ return preferences.get("recent_tab", ""); }
|
||||
public void setLastOpenedTab(String tabId){ preferences.put("recent_tab", tabId); }
|
||||
// Patches
|
||||
public String getKeysLocation(){ return preferences.get("keys", ""); }
|
||||
public void setKeysLocation(String path){ preferences.put("keys", path); }
|
||||
|
||||
public String getPatchesSaveToLocation(){ return FilesHelper.getRealFolder(preferences.get("patches_saveto", System.getProperty("user.home"))); }
|
||||
public void setPatchesSaveToLocation(String value){ preferences.put("patches_saveto", value); }
|
||||
}
|
||||
|
|
|
@ -43,7 +43,7 @@ public class NSLMainController implements Initializable {
|
|||
@FXML
|
||||
private TabPane mainTabPane;
|
||||
@FXML
|
||||
private Tab GamesTabHolder, RCMTabHolder, SMTabHolder;
|
||||
private Tab GamesTabHolder, RCMTabHolder, SMTabHolder, PatchesTabHolder;
|
||||
|
||||
@FXML
|
||||
private GamesController GamesTabController;
|
||||
|
@ -55,6 +55,8 @@ public class NSLMainController implements Initializable {
|
|||
private RcmController RcmTabController;
|
||||
@FXML
|
||||
private NxdtController NXDTabController;
|
||||
@FXML
|
||||
private PatchesController PatchesTabController;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
|
@ -71,7 +73,7 @@ public class NSLMainController implements Initializable {
|
|||
if (AppPreferences.getInstance().getAutoCheckUpdates()){
|
||||
checkForUpdates();
|
||||
}
|
||||
|
||||
if (! AppPreferences.getInstance().take()) mainTabPane.getTabs().remove(3);
|
||||
openLastOpenedTab();
|
||||
}
|
||||
private void checkForUpdates(){
|
||||
|
@ -127,6 +129,8 @@ public class NSLMainController implements Initializable {
|
|||
public RcmController getRcmCtrlr(){ return RcmTabController; }
|
||||
|
||||
public NxdtController getNXDTabController(){ return NXDTabController; }
|
||||
|
||||
public PatchesController getPatchesTabController(){ return PatchesTabController; }
|
||||
/**
|
||||
* Save preferences before exit
|
||||
* */
|
||||
|
@ -136,7 +140,7 @@ public class NSLMainController implements Initializable {
|
|||
SplitMergeTabController.updatePreferencesOnExit(); // NOTE: This shit above should be re-written to similar pattern
|
||||
RcmTabController.updatePreferencesOnExit();
|
||||
NXDTabController.updatePreferencesOnExit();
|
||||
|
||||
PatchesTabController.updatePreferencesOnExit();
|
||||
saveLastOpenedTab();
|
||||
}
|
||||
|
||||
|
@ -152,6 +156,9 @@ public class NSLMainController implements Initializable {
|
|||
case "SMTabHolder":
|
||||
mainTabPane.getSelectionModel().select(SMTabHolder);
|
||||
break;
|
||||
case "PatchesTabHolder":
|
||||
mainTabPane.getSelectionModel().select(PatchesTabHolder);
|
||||
break;
|
||||
}
|
||||
}
|
||||
private void saveLastOpenedTab(){
|
||||
|
|
225
src/main/java/nsusbloader/Controllers/PatchesController.java
Normal file
225
src/main/java/nsusbloader/Controllers/PatchesController.java
Normal file
|
@ -0,0 +1,225 @@
|
|||
/*
|
||||
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.Controllers;
|
||||
|
||||
import javafx.fxml.FXML;
|
||||
import javafx.fxml.Initializable;
|
||||
import javafx.scene.input.DragEvent;
|
||||
import javafx.scene.input.TransferMode;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
import java.util.List;
|
||||
import java.util.ResourceBundle;
|
||||
import javafx.scene.control.Button;
|
||||
import javafx.scene.control.Label;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.scene.layout.VBox;
|
||||
import javafx.stage.DirectoryChooser;
|
||||
import javafx.stage.FileChooser;
|
||||
import nsusbloader.AppPreferences;
|
||||
import nsusbloader.FilesHelper;
|
||||
import nsusbloader.MediatorControl;
|
||||
import nsusbloader.NSLDataTypes.EModule;
|
||||
import nsusbloader.ServiceWindow;
|
||||
import nsusbloader.Utilities.patches.es.EsPatchMaker;
|
||||
|
||||
// TODO: CLI SUPPORT
|
||||
public class PatchesController implements Initializable {
|
||||
@FXML
|
||||
private VBox patchesToolPane;
|
||||
@FXML
|
||||
private Button selFwFolderBtn, selProdKeysBtn, makeEsBtn;
|
||||
@FXML
|
||||
private Label shortNameFirmwareLbl, locationFirmwareLbl, saveToLbl, shortNameKeysLbl, locationKeysLbl, statusLbl;
|
||||
private Thread workThread;
|
||||
|
||||
private String previouslyOpenedPath;
|
||||
private ResourceBundle resourceBundle;
|
||||
private Region convertRegion;
|
||||
|
||||
@Override
|
||||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
this.resourceBundle = resourceBundle;
|
||||
this.previouslyOpenedPath = System.getProperty("user.home");
|
||||
|
||||
String myRegexp;
|
||||
if (File.separator.equals("/"))
|
||||
myRegexp = "^.+/";
|
||||
else
|
||||
myRegexp = "^.+\\\\";
|
||||
locationFirmwareLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
|
||||
shortNameFirmwareLbl.setText(updatedText.replaceAll(myRegexp, "")));
|
||||
|
||||
locationKeysLbl.textProperty().addListener((observableValue, currentText, updatedText) ->
|
||||
shortNameKeysLbl.setText(updatedText.replaceAll(myRegexp, "")));
|
||||
|
||||
convertRegion = new Region();
|
||||
convertRegion.getStyleClass().add("regionCake");
|
||||
makeEsBtn.setGraphic(convertRegion);
|
||||
|
||||
AppPreferences preferences = AppPreferences.getInstance();
|
||||
String keysLocation = preferences.getKeysLocation();
|
||||
File keysFile = new File(keysLocation);
|
||||
|
||||
if (keysFile.exists() && keysFile.isFile()) {
|
||||
locationKeysLbl.setText(keysLocation);
|
||||
}
|
||||
|
||||
saveToLbl.setText(preferences.getPatchesSaveToLocation());
|
||||
//makeEsBtn.disableProperty().bind(Bindings.isEmpty(locationFirmwareLbl.textProperty()));
|
||||
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
||||
}
|
||||
|
||||
/**
|
||||
* Drag-n-drop support (dragOver consumer)
|
||||
* */
|
||||
@FXML
|
||||
private void handleDragOver(DragEvent event){
|
||||
if (event.getDragboard().hasFiles())
|
||||
event.acceptTransferModes(TransferMode.ANY);
|
||||
event.consume();
|
||||
}
|
||||
/**
|
||||
* Drag-n-drop support (drop consumer)
|
||||
* */
|
||||
@FXML
|
||||
private void handleDrop(DragEvent event){
|
||||
List<File> filesDropped = event.getDragboard().getFiles();
|
||||
for (File file : filesDropped){
|
||||
if (file.isDirectory()) {
|
||||
locationFirmwareLbl.setText(file.getAbsolutePath());
|
||||
continue;
|
||||
}
|
||||
String fileName = file.getName().toLowerCase();
|
||||
if ((fileName.endsWith(".dat")) ||
|
||||
(fileName.endsWith(".keys") &&
|
||||
! fileName.equals("dev.keys") &&
|
||||
! fileName.equals("title.keys")))
|
||||
locationKeysLbl.setText(file.getAbsolutePath());
|
||||
}
|
||||
event.setDropCompleted(true);
|
||||
event.consume();
|
||||
}
|
||||
@FXML
|
||||
private void selectFirmware(){
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle(resourceBundle.getString("tabPatches_Lbl_Firmware"));
|
||||
directoryChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
|
||||
File firmware = directoryChooser.showDialog(patchesToolPane.getScene().getWindow());
|
||||
if (firmware == null)
|
||||
return;
|
||||
locationFirmwareLbl.setText(firmware.getAbsolutePath());
|
||||
}
|
||||
@FXML
|
||||
private void selectSaveTo(){
|
||||
DirectoryChooser directoryChooser = new DirectoryChooser();
|
||||
directoryChooser.setTitle(resourceBundle.getString("tabSplMrg_Btn_SelectFolder"));
|
||||
directoryChooser.setInitialDirectory(new File(FilesHelper.getRealFolder(previouslyOpenedPath)));
|
||||
File saveToDir = directoryChooser.showDialog(patchesToolPane.getScene().getWindow());
|
||||
if (saveToDir == null)
|
||||
return;
|
||||
saveToLbl.setText(saveToDir.getAbsolutePath());
|
||||
}
|
||||
@FXML
|
||||
private void selectProdKeys(){
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("tabPatches_Lbl_Keys"));
|
||||
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()) {
|
||||
locationKeysLbl.setText(keys.getAbsolutePath());
|
||||
}
|
||||
}
|
||||
|
||||
private void makeEs(){
|
||||
if (locationFirmwareLbl.getText().isEmpty() || locationKeysLbl.getText().isEmpty()){
|
||||
ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
|
||||
resourceBundle.getString("tabPatches_ServiceWindowMessage"));
|
||||
return;
|
||||
}
|
||||
|
||||
if (workThread != null && workThread.isAlive())
|
||||
return;
|
||||
statusLbl.setText("");
|
||||
|
||||
if (MediatorControl.getInstance().getTransferActive()) {
|
||||
ServiceWindow.getErrorNotification(resourceBundle.getString("windowTitleError"),
|
||||
resourceBundle.getString("windowBodyPleaseStopOtherProcessFirst"));
|
||||
return;
|
||||
}
|
||||
|
||||
EsPatchMaker esPatchMaker = new EsPatchMaker(locationFirmwareLbl.getText(), locationKeysLbl.getText(),
|
||||
saveToLbl.getText());
|
||||
workThread = new Thread(esPatchMaker);
|
||||
|
||||
workThread.setDaemon(true);
|
||||
workThread.start();
|
||||
}
|
||||
private void interruptProcessOfPatchMaking(){
|
||||
if (workThread == null || ! workThread.isAlive())
|
||||
return;
|
||||
|
||||
workThread.interrupt();
|
||||
}
|
||||
|
||||
public void notifyThreadStarted(boolean isActive, EModule type) {
|
||||
if (! type.equals(EModule.PATCHES)) {
|
||||
patchesToolPane.setDisable(isActive);
|
||||
return;
|
||||
}
|
||||
|
||||
convertRegion.getStyleClass().clear();
|
||||
|
||||
if (isActive) {
|
||||
MediatorControl.getInstance().getContoller().logArea.clear();
|
||||
convertRegion.getStyleClass().add("regionStop");
|
||||
|
||||
makeEsBtn.setOnAction(e-> interruptProcessOfPatchMaking());
|
||||
makeEsBtn.setText(resourceBundle.getString("btn_Stop"));
|
||||
makeEsBtn.getStyleClass().remove("buttonUp");
|
||||
makeEsBtn.getStyleClass().add("buttonStop");
|
||||
}
|
||||
else {
|
||||
convertRegion.getStyleClass().add("regionCake");
|
||||
|
||||
makeEsBtn.setOnAction(actionEvent -> makeEs());
|
||||
makeEsBtn.setText(resourceBundle.getString("tabPatches_Btn_MakeEs"));
|
||||
makeEsBtn.getStyleClass().remove("buttonStop");
|
||||
makeEsBtn.getStyleClass().add("buttonUp");
|
||||
}
|
||||
}
|
||||
|
||||
public void setOneLineStatus(boolean statusSuccess){
|
||||
if (statusSuccess)
|
||||
statusLbl.setText(resourceBundle.getString("done_txt"));
|
||||
else
|
||||
statusLbl.setText(resourceBundle.getString("failure_txt"));
|
||||
}
|
||||
|
||||
void updatePreferencesOnExit(){
|
||||
AppPreferences.getInstance().setPatchesSaveToLocation(saveToLbl.getText());
|
||||
if (locationKeysLbl.getText().isEmpty())
|
||||
return;
|
||||
AppPreferences.getInstance().setKeysLocation(locationKeysLbl.getText());
|
||||
}
|
||||
|
||||
}
|
|
@ -40,11 +40,12 @@ public class MediatorControl {
|
|||
}
|
||||
|
||||
public NSLMainController getContoller(){ return mainController; }
|
||||
public GamesController getGamesController(){ return mainController.getGamesCtrlr(); };
|
||||
public SettingsController getSettingsController(){ return mainController.getSettingsCtrlr(); };
|
||||
public SplitMergeController getSplitMergeController(){ return mainController.getSmCtrlr(); };
|
||||
public RcmController getRcmController(){ return mainController.getRcmCtrlr(); };
|
||||
public NxdtController getNxdtController(){ return mainController.getNXDTabController(); };
|
||||
public GamesController getGamesController(){ return mainController.getGamesCtrlr(); }
|
||||
public SettingsController getSettingsController(){ return mainController.getSettingsCtrlr(); }
|
||||
public SplitMergeController getSplitMergeController(){ return mainController.getSmCtrlr(); }
|
||||
public RcmController getRcmController(){ return mainController.getRcmCtrlr(); }
|
||||
public NxdtController getNxdtController(){ return mainController.getNXDTabController(); }
|
||||
public PatchesController getPatchesController(){ return mainController.getPatchesTabController(); }
|
||||
|
||||
public ResourceBundle getResourceBundle(){
|
||||
return mainController.getResourceBundle();
|
||||
|
@ -56,6 +57,7 @@ public class MediatorControl {
|
|||
getSplitMergeController().notifyThreadStarted(isActive, appModuleType);
|
||||
getRcmController().notifyThreadStarted(isActive, appModuleType);
|
||||
getNxdtController().notifyThreadStarted(isActive, appModuleType);
|
||||
getPatchesController().notifyThreadStarted(isActive, appModuleType);
|
||||
}
|
||||
public synchronized boolean getTransferActive() { return this.isTransferActive.get(); }
|
||||
}
|
||||
|
|
|
@ -112,6 +112,8 @@ public class MessagesConsumer extends AnimationTimer {
|
|||
case SPLIT_MERGE_TOOL:
|
||||
MediatorControl.getInstance().getSplitMergeController().setOneLineStatus(oneLinerStatus.get());
|
||||
break;
|
||||
case PATCHES:
|
||||
MediatorControl.getInstance().getPatchesController().setOneLineStatus(oneLinerStatus.get());
|
||||
}
|
||||
this.stop();
|
||||
}
|
||||
|
|
|
@ -22,5 +22,6 @@ public enum EModule {
|
|||
USB_NET_TRANSFERS,
|
||||
SPLIT_MERGE_TOOL,
|
||||
RCM,
|
||||
NXDT
|
||||
NXDT,
|
||||
PATCHES
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@ import nsusbloader.Controllers.NSLMainController;
|
|||
import nsusbloader.cli.CommandLineInterface;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.Objects;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class NSLMain extends Application {
|
||||
|
@ -47,10 +48,10 @@ public class NSLMain extends Application {
|
|||
Parent root = loader.load();
|
||||
|
||||
primaryStage.getIcons().addAll(
|
||||
new Image(getClass().getResourceAsStream("/res/app_icon32x32.png")),
|
||||
new Image(getClass().getResourceAsStream("/res/app_icon48x48.png")),
|
||||
new Image(getClass().getResourceAsStream("/res/app_icon64x64.png")),
|
||||
new Image(getClass().getResourceAsStream("/res/app_icon128x128.png"))
|
||||
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon32x32.png"))),
|
||||
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon48x48.png"))),
|
||||
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon64x64.png"))),
|
||||
new Image(Objects.requireNonNull(getClass().getResourceAsStream("/res/app_icon128x128.png")))
|
||||
);
|
||||
|
||||
primaryStage.setTitle("NS-USBloader "+appVersion);
|
||||
|
|
|
@ -0,0 +1,526 @@
|
|||
/*
|
||||
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.es;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import nsusbloader.Main;
|
||||
import nsusbloader.NSLMain;
|
||||
|
||||
public class BinToAsmPrinter {
|
||||
static {
|
||||
boolean notWindows = ! System.getProperty("os.name").toLowerCase().contains("windows");
|
||||
|
||||
if(notWindows && NSLMain.isCli){
|
||||
ANSI_RESET = "\u001B[0m";
|
||||
ANSI_GREEN = "\u001B[32m";
|
||||
ANSI_BLUE = "\u001B[34m";
|
||||
ANSI_YELLOW = "\u001B[33m";
|
||||
ANSI_PURPLE = "\u001B[35m";
|
||||
ANSI_CYAN = "\u001B[36m";
|
||||
ANSI_RED = "\u001B[31m";
|
||||
}
|
||||
else {
|
||||
ANSI_RESET = ANSI_RED = ANSI_GREEN = ANSI_BLUE = ANSI_YELLOW = ANSI_PURPLE = ANSI_CYAN = "";
|
||||
}
|
||||
}
|
||||
private static final String ANSI_RESET;
|
||||
private static final String ANSI_RED;
|
||||
private static final String ANSI_GREEN;
|
||||
private static final String ANSI_BLUE;
|
||||
private static final String ANSI_YELLOW;
|
||||
private static final String ANSI_PURPLE;
|
||||
private static final String ANSI_CYAN;
|
||||
|
||||
public static String print(int instructionExpression, int offset){
|
||||
if (instructionExpression == 0xd503201f)
|
||||
return printNOP(instructionExpression);
|
||||
|
||||
if ((instructionExpression & 0x7FE0FFE0) == 0x2A0003E0) {
|
||||
return printMOVRegister(instructionExpression);
|
||||
}
|
||||
|
||||
switch ((instructionExpression >> 23 & 0b011111111)){
|
||||
case 0xA5:
|
||||
return printMOV(instructionExpression);
|
||||
case 0x62:
|
||||
if (((instructionExpression & 0x1f) == 0x1f)){
|
||||
return printCMN(instructionExpression, offset);
|
||||
}
|
||||
}
|
||||
|
||||
switch (instructionExpression >> 24 & 0xff) {
|
||||
case 0x34:
|
||||
case 0xb4:
|
||||
return printCBZ(instructionExpression, offset);
|
||||
case 0xb5:
|
||||
case 0x35:
|
||||
return printCBNZ(instructionExpression, offset);
|
||||
case 0x36:
|
||||
case 0xb6:
|
||||
return printTBZ(instructionExpression, offset);
|
||||
case 0x54:
|
||||
return printBConditional(instructionExpression, offset);
|
||||
}
|
||||
switch ((instructionExpression >> 26 & 0b111111)) {
|
||||
case 0x5:
|
||||
return printB(instructionExpression, offset);
|
||||
case 0x25:
|
||||
return printBL(instructionExpression, offset);
|
||||
}
|
||||
|
||||
return printUnknown(instructionExpression);
|
||||
}
|
||||
public static String printSimplified(int instructionExpression, int offset){
|
||||
if (instructionExpression == 0xd503201f)
|
||||
return printNOPSimplified(instructionExpression, offset);
|
||||
|
||||
if ((instructionExpression & 0x7FE0FFE0) == 0x2A0003E0) {
|
||||
return printMOVRegisterSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
switch (instructionExpression >> 22 & 0b1011111111) {
|
||||
case 0x2e5:
|
||||
return printLRDImmUnsignSimplified(instructionExpression, offset);
|
||||
case 0xe5:
|
||||
return printLRDBImmUnsignSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
if ((instructionExpression >> 21 & 0x7FF) == 0x1C2)
|
||||
return printLDURBSimplified(instructionExpression, offset);
|
||||
|
||||
// same to (afterJumpExpression >> 23 & 0x1F9) != 0xA1
|
||||
switch (instructionExpression >> 22 & 0x1FF){
|
||||
case 0xA3: // 0b10100011
|
||||
case 0xA7: // 0b10100111
|
||||
case 0xA5: // 0b10100101
|
||||
return printLDPSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
switch ((instructionExpression >> 23 & 0xff)){
|
||||
case 0xA5:
|
||||
return printMOVSimplified(instructionExpression, offset);
|
||||
case 0x22:
|
||||
return printADDSimplified(instructionExpression, offset);
|
||||
case 0x62:
|
||||
if (((instructionExpression & 0x1f) == 0x1f)){
|
||||
return printCMNSimplified(instructionExpression, offset);
|
||||
}
|
||||
case 0xA2:
|
||||
return printSUBSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
switch (instructionExpression >> 24 & 0xff) {
|
||||
case 0x34:
|
||||
case 0xb4:
|
||||
return printCBZSimplified(instructionExpression, offset);
|
||||
case 0xb5:
|
||||
case 0x35:
|
||||
return printCBNZSimplified(instructionExpression, offset);
|
||||
case 0x36:
|
||||
case 0xb6:
|
||||
return printTBZSimplified(instructionExpression, offset);
|
||||
case 0x54:
|
||||
return printBConditionalSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
switch ((instructionExpression >> 26 & 0b111111)) {
|
||||
case 0x5:
|
||||
return printBSimplified(instructionExpression, offset);
|
||||
case 0x25:
|
||||
return printBLSimplified(instructionExpression, offset);
|
||||
}
|
||||
return printUnknownSimplified(instructionExpression, offset);
|
||||
}
|
||||
|
||||
private static String printCBZ(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
|
||||
"CBZ <?t>, <label> |.....CBZ signature......|\n" +
|
||||
ANSI_CYAN + " sf 0 1 1 0 1 0 0 |imm19..........................................................||Rd.............|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n\n"+
|
||||
ANSI_YELLOW + "CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
|
||||
private static String printCBNZ(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
|
||||
"CBNZ <?t>, <label> |.....CBZ signature......|\n" +
|
||||
ANSI_CYAN + " sf 0 1 1 0 1 0 |imm19..........................................................||Rd.............|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n\n"+
|
||||
ANSI_YELLOW + "CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
private static String printCMN(int instructionExpression, int offset){
|
||||
int Rn = instructionExpression >> 5 & 0x1F;
|
||||
int imm = instructionExpression >> 10 & 0xFFF;
|
||||
|
||||
return String.format(ANSI_YELLOW + "sf == 0 ? <Wt> else <Xt>\n" +
|
||||
"CMN <?n>, <label> |.....CMN signature...........| |..CMN signature.|\n" +
|
||||
ANSI_CYAN+" sf 0 1 1 0 0 0 1 0 |imm12......................................||Rn.............| 1 1 1 1 1" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n\n" +
|
||||
ANSI_YELLOW + "CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
|
||||
}
|
||||
|
||||
private static String printB(int instructionExpression, int offset){
|
||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(ANSI_YELLOW+"B <label> |....B signature...|\n" +
|
||||
" "+ANSI_CYAN+" 0 0 0 1 0 1 |imm26...................................................................................|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
((instructionExpression >> 26 & 0b111111) == 5)?"B":"Some weird stuff",
|
||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||
}
|
||||
|
||||
|
||||
private static String printBL(int instructionExpression, int offset){
|
||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(ANSI_YELLOW+"BL <label> |...BL signature...|\n" +
|
||||
" "+ANSI_CYAN+" 1 0 0 1 0 1 |imm26...................................................................................|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
((instructionExpression >> 26 & 0b111111) == 25)?"BL":"Some weird stuff",
|
||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||
}
|
||||
|
||||
|
||||
private static String printMOV(int instructionExpression){
|
||||
int imm16 = instructionExpression >> 5 & 0xFFFF;
|
||||
int sfHw = (instructionExpression >> 22 & 1);
|
||||
|
||||
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
|
||||
"MOV <?t>, <label> |.....MOV signature...........|\n" +
|
||||
ANSI_CYAN +" sf 1 0 1 0 0 1 0 1 |hw...|imm16.................................................||Rd.............|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
|
||||
}
|
||||
|
||||
private static String printMOVRegister(int instructionExpression){
|
||||
String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X";
|
||||
int Rm = instructionExpression >> 16 & 0xF;
|
||||
int Rd = instructionExpression & 0xF;
|
||||
|
||||
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
|
||||
"MOV (register) <?d>, <?m> |.....MOV (register) signature.......|\n" +
|
||||
ANSI_CYAN +" sf 0 1 0 1 0 1 0 0 0 0 |Rm..............| 0 0 0 0 0 0 1 1 1 1 1 |Rd.............|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "MOV(reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
sfHw, Rm, sfHw, Rd);
|
||||
}
|
||||
|
||||
private static String printNOP(int instructionExpression){
|
||||
return String.format(
|
||||
ANSI_YELLOW+"NOP |.....NOP signature..........................................................................................|\n" +
|
||||
ANSI_CYAN +" 1 1 0 1 0 1 0 1 0 0 0 0 0 0 1 1 0 0 1 0 0 0 0 0 0 0 0 1 1 1 1 1 " + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n"+
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "%s" + ANSI_RESET + "\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(instructionExpression == 0xd503201f)?"NOP":"Some weird stuff");
|
||||
}
|
||||
|
||||
private static String printTBZ(int instructionExpression, int offset){
|
||||
int xwSelector = (instructionExpression >> 31 & 1);
|
||||
int imm = instructionExpression >> 18 & 0b11111;
|
||||
int Rt = instructionExpression & 0b11111;
|
||||
int label = (offset + (instructionExpression >> 5 & 0x3fff) * 4) & 0xfffff;
|
||||
|
||||
//System.out.printf("\nInstruction: %x\n", instructionExpression);
|
||||
return String.format(ANSI_YELLOW + "sf == 0 && hw == 0x ? <Wt> else <Xt>\n" +
|
||||
"TBZ <?t>,#<imm>, <label> |.....TBZ signature.......|\n" +
|
||||
ANSI_CYAN+" b5 0 1 1 0 1 1 0 |b40.............|imm14.........................................||Rt.............|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
|
||||
}
|
||||
|
||||
private static String printBConditional(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
ANSI_YELLOW+"B.%s <label> |...B.cond signature.......|\n" +
|
||||
ANSI_CYAN+" 0 1 0 1 0 1 0 0 |imm19..........................................................| 0 |.condit...|" + ANSI_RESET + "\n" +
|
||||
ANSI_GREEN +" 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n" +
|
||||
ANSI_YELLOW + "B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
getBConditionalMarker(instructionExpression & 0xf),
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression),
|
||||
getBConditionalMarker(instructionExpression & 0xf),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
private static String printUnknown(int instructionExpression){
|
||||
return String.format(ANSI_RED + " 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 " + ANSI_RESET + "\n" +
|
||||
"Instruction (BE) : %s | %s\n",
|
||||
intAsBinString(instructionExpression), intAsHexString(instructionExpression));
|
||||
}
|
||||
|
||||
private static String getBConditionalMarker(int cond){
|
||||
switch (cond){
|
||||
case 0b0000: return "EQ";
|
||||
case 0b0001: return "NE";
|
||||
case 0b0010: return "CS";
|
||||
case 0b0011: return "CC";
|
||||
case 0b0100: return "MI";
|
||||
case 0b0101: return "PL";
|
||||
case 0b0110: return "VS";
|
||||
case 0b0111: return "VC";
|
||||
case 0b1000: return "HI";
|
||||
case 0b1001: return "LS";
|
||||
case 0b1010: return "GE";
|
||||
case 0b1011: return "LT";
|
||||
case 0b1100: return "GT";
|
||||
case 0b1101: return "LE";
|
||||
case 0b1110: return "AL";
|
||||
default: return "??";
|
||||
}
|
||||
/*
|
||||
"__________________CheatSheet_____________________________________\n"+
|
||||
"0000 | EQ | Z set | equal\n"+
|
||||
"0001 | NE | Z clear | not equal\n"+
|
||||
"0010 | CS | C set | unsigned higher or same\n"+
|
||||
"0011 | CC | C clear | unsigned lower\n"+
|
||||
"0100 | MI | N set | negative\n"+
|
||||
"0101 | PL | N clear | positive or zero\n"+
|
||||
"0110 | VS | V set | overflow\n"+
|
||||
"0111 | VC | V clear | no overflow\n"+
|
||||
"1000 | HI | C set & V clear | unsigned higher\n"+
|
||||
"1001 | LS | C clear or Z set | unsigned lower or same\n"+
|
||||
"1010 | GE | N equals V | greater or equal\n"+
|
||||
"1011 | LT | N not equals V | less than\n"+
|
||||
"1100 | GT | Z clear AND (N equals V) | greater that\n"+
|
||||
"1101 | LE | Z set OR (N not equals V) | less than or equal\n"+
|
||||
"1110 | AL | (ignored) | always\n";
|
||||
*/
|
||||
}
|
||||
|
||||
|
||||
private static String printCBZSimplified(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
private static String printCBNZSimplified(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
private static String printBSimplified(int instructionExpression, int offset){
|
||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||
}
|
||||
|
||||
|
||||
private static String printBLSimplified(int instructionExpression, int offset){
|
||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " BL " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static String printMOVSimplified(int instructionExpression, int offset){
|
||||
int imm16 = instructionExpression >> 5 & 0xFFFF;
|
||||
int sfHw = (instructionExpression >> 22 & 1);
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
|
||||
}
|
||||
|
||||
private static String printNOPSimplified(int instructionExpression, int offset){
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||
}
|
||||
|
||||
private static String printTBZSimplified(int instructionExpression, int offset){
|
||||
int xwSelector = (instructionExpression >> 31 & 1);
|
||||
int imm = instructionExpression >> 18 & 0b11111;
|
||||
int Rt = instructionExpression & 0b11111;
|
||||
int label = (offset + (instructionExpression >> 5 & 0x3fff) * 4) & 0xfffff;
|
||||
|
||||
//System.out.printf("\nInstruction: %x\n", instructionExpression);
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
|
||||
}
|
||||
|
||||
private static String printBConditionalSimplified(int instructionExpression, int offset){
|
||||
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
getBConditionalMarker(instructionExpression & 0xf),
|
||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||
}
|
||||
|
||||
private static String printADDSimplified(int instructionExpression, int offset){ //ADD (immediate)
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ADD . . . \n"+ ANSI_RESET,
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||
}
|
||||
private static String printLDPSimplified(int instructionExpression, int offset){
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDP . . . \n"+ ANSI_RESET,
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||
}
|
||||
private static String printLDURBSimplified(int instructionExpression, int offset){
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDURB . . . \n"+ ANSI_RESET,
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||
}
|
||||
private static String printSUBSimplified(int instructionExpression, int offset){
|
||||
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
|
||||
int Rt = instructionExpression & 0x1f;
|
||||
int Rn = instructionExpression >> 5 & 0x1F;
|
||||
int imm12 = instructionExpression >> 10 & 0xFFF; // unsigned only
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " SUB (imm) " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
wx, Rt, wx, Rn, imm12);
|
||||
}
|
||||
|
||||
|
||||
private static String printMOVRegisterSimplified(int instructionExpression, int offset){ //ADD (immediate)
|
||||
String sfHw = (instructionExpression >> 31 & 1) == 0 ? "W" : "X";
|
||||
int Rm = instructionExpression >> 16 & 0xF;
|
||||
int Rd = instructionExpression & 0xF;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
sfHw, Rm, sfHw, Rd);
|
||||
}
|
||||
|
||||
private static String printCMNSimplified(int instructionExpression, int offset){
|
||||
int Rn = instructionExpression >> 5 & 0x1F;
|
||||
int imm = instructionExpression >> 10 & 0xFFF;
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
|
||||
}
|
||||
|
||||
private static String printLRDImmUnsignSimplified(int instructionExpression, int offset){
|
||||
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
|
||||
int Rt = instructionExpression & 0x1f;
|
||||
int Rn = instructionExpression >> 5 & 0xF;
|
||||
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
|
||||
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDR(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
wx, Rt, wx, Rn, imm12);
|
||||
}
|
||||
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
|
||||
String wx = (instructionExpression >> 31 == 0) ? "W" : "X";
|
||||
int Rt = instructionExpression & 0x1f;
|
||||
int Rn = instructionExpression >> 5 & 0xF;
|
||||
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
|
||||
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDRB(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||
wx, Rt, wx, Rn, imm12);
|
||||
}
|
||||
|
||||
|
||||
|
||||
private static String printUnknownSimplified(int instructionExpression, int offset){
|
||||
return String.format(
|
||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(Integer.reverseBytes(instructionExpression)) +"\n",
|
||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||
}
|
||||
|
||||
private static String intAsBinString(int number) {
|
||||
StringBuilder result = new StringBuilder();
|
||||
for(int i = 31; i >= 0 ; i--) {
|
||||
int mask = 1 << i;
|
||||
result.append((number & mask) != 0 ? "1" : "0");
|
||||
result.append(" ");
|
||||
if (i % 4 == 0)
|
||||
result.append(" ");
|
||||
}
|
||||
result.replace(result.length() - 1, result.length(), "");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
private static String intAsHexString(int number) {
|
||||
number = Integer.reverseBytes(number);
|
||||
StringBuilder result = new StringBuilder();
|
||||
for(int i = 0; i <= 3 ; i++) {
|
||||
int mask = 0xff << i*8;
|
||||
result.append(String.format("%02x", (byte)((number & mask) >> i*8)));
|
||||
result.append(" ");
|
||||
}
|
||||
result.replace(result.length() - 1, result.length(), "");
|
||||
|
||||
return result.toString();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,50 @@
|
|||
/*
|
||||
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.es;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import libKonogonka.Tools.NCA.NCAProvider;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Callable;
|
||||
|
||||
class EsNcaSearchTask implements Callable<NCAProvider> {
|
||||
private final List<NCAProvider> ncaProviders;
|
||||
|
||||
EsNcaSearchTask(List<NCAProvider> ncaProviders){
|
||||
this.ncaProviders = ncaProviders;
|
||||
}
|
||||
|
||||
@Override
|
||||
public NCAProvider call() {
|
||||
try {
|
||||
for (NCAProvider ncaProvider : ncaProviders) {
|
||||
String titleId = Converter.byteArrToHexStringAsLE(ncaProvider.getTitleId());
|
||||
if (titleId.startsWith("0100000000000033") && ncaProvider.getContentType() == 0) {
|
||||
return ncaProvider;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
174
src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java
Normal file
174
src/main/java/nsusbloader/Utilities/patches/es/EsPatch.java
Normal file
|
@ -0,0 +1,174 @@
|
|||
/*
|
||||
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/>.
|
||||
---
|
||||
Based on ES-AutoIPS.py patch script made by GBATemp member MrDude.
|
||||
Taken from: https://gbatemp.net/threads/info-on-sha-256-hashes-on-fs-patches.581550/
|
||||
*/
|
||||
package nsusbloader.Utilities.patches.es;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import libKonogonka.Tools.NCA.NCAProvider;
|
||||
import libKonogonka.Tools.NSO.NSO0Header;
|
||||
import libKonogonka.Tools.NSO.NSO0Provider;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
import nsusbloader.Utilities.patches.es.finders.HeuristicEsWizard;
|
||||
|
||||
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.util.Arrays;
|
||||
|
||||
public class EsPatch {
|
||||
private final NCAProvider ncaProvider;
|
||||
private final String saveToLocation;
|
||||
private final ILogPrinter logPrinter;
|
||||
|
||||
private Long fwVersion;
|
||||
private String buildId;
|
||||
private byte[] _textSection;
|
||||
|
||||
private HeuristicEsWizard wizard;
|
||||
|
||||
EsPatch(NCAProvider ncaProvider, String saveToLocation, ILogPrinter logPrinter) throws Exception{
|
||||
this.ncaProvider = ncaProvider;
|
||||
this.saveToLocation = saveToLocation + File.separator +
|
||||
"atmosphere" + File.separator + "exefs_patches" + File.separator + "es_patches";
|
||||
this.logPrinter = logPrinter;
|
||||
|
||||
getPlainFirmwareVersion();
|
||||
NSO0Provider nso0Provider = new NSO0Provider(ncaProvider.getNCAContentProvider(0).getPfs0().getStreamProducer(0));
|
||||
getBuildId(nso0Provider);
|
||||
getTextSection(nso0Provider);
|
||||
findAllOffsets();
|
||||
mkDirs();
|
||||
writeFile();
|
||||
logPrinter.print(" == Debug information ==\n"+wizard.getDebug(), EMsgType.NULL);
|
||||
}
|
||||
private void getPlainFirmwareVersion() throws Exception{
|
||||
fwVersion = Long.parseLong(""+ncaProvider.getSdkVersion()[3]+ncaProvider.getSdkVersion()[2]
|
||||
+ncaProvider.getSdkVersion()[1] +ncaProvider.getSdkVersion()[0]);
|
||||
logPrinter.print("Internal firmware version: "+ncaProvider.getSdkVersion()[3] +"."+ncaProvider.getSdkVersion()[2] +"."+ncaProvider.getSdkVersion()[1] +"."+ncaProvider.getSdkVersion()[0], EMsgType.INFO);
|
||||
}
|
||||
private void getBuildId(NSO0Provider nso0Provider) throws Exception{
|
||||
NSO0Header nso0DecompressedHeader = nso0Provider.getAsDecompressedNSO0().getHeader();
|
||||
byte[] buildIdBytes = nso0DecompressedHeader.getModuleId();
|
||||
buildId = Converter.byteArrToHexStringAsLE(buildIdBytes).substring(0, 40).toUpperCase();
|
||||
logPrinter.print("Build ID: "+buildId, EMsgType.INFO);
|
||||
}
|
||||
private void getTextSection(NSO0Provider nso0Provider) throws Exception{
|
||||
_textSection = nso0Provider.getAsDecompressedNSO0().getTextRaw();
|
||||
}
|
||||
private void findAllOffsets() throws Exception{
|
||||
this.wizard = new HeuristicEsWizard(fwVersion, _textSection);
|
||||
String errorsAndNotes = wizard.getErrorsAndNotes();
|
||||
if (errorsAndNotes.length() > 0)
|
||||
logPrinter.print(errorsAndNotes, EMsgType.WARNING);
|
||||
}
|
||||
private void mkDirs(){
|
||||
File parentFolder = new File(saveToLocation);
|
||||
parentFolder.mkdirs();
|
||||
}
|
||||
|
||||
private void writeFile() throws Exception{
|
||||
String patchFileLocation = saveToLocation + File.separator + buildId + ".ips";
|
||||
int offset1 = wizard.getOffset1();
|
||||
int offset2 = wizard.getOffset2();
|
||||
int offset3 = wizard.getOffset3();
|
||||
|
||||
ByteBuffer handyEsPatch = ByteBuffer.allocate(0x23).order(ByteOrder.LITTLE_ENDIAN);
|
||||
handyEsPatch.put(getHeader());
|
||||
if (offset1 > 0) {
|
||||
logPrinter.print("Patch component 1 will be used", EMsgType.PASS);
|
||||
handyEsPatch.put(getPatch1(offset1));
|
||||
}
|
||||
if (offset2 > 0) {
|
||||
logPrinter.print("Patch component 2 will be used", EMsgType.PASS);
|
||||
handyEsPatch.put(getPatch2(offset2));
|
||||
}
|
||||
if (offset3 > 0) {
|
||||
logPrinter.print("Patch component 3 will be used", EMsgType.PASS);
|
||||
handyEsPatch.put(getPatch3(offset3));
|
||||
}
|
||||
handyEsPatch.put(getFooter());
|
||||
|
||||
try (BufferedOutputStream stream = new BufferedOutputStream(
|
||||
Files.newOutputStream(new File(patchFileLocation).toPath()))){
|
||||
stream.write(handyEsPatch.array());
|
||||
}
|
||||
logPrinter.print("Patch created at "+patchFileLocation, EMsgType.PASS);
|
||||
}
|
||||
private byte[] getHeader(){
|
||||
return "PATCH".getBytes(StandardCharsets.US_ASCII);
|
||||
}
|
||||
private byte[] getFooter(){
|
||||
return "EOF".getBytes(StandardCharsets.US_ASCII);
|
||||
}
|
||||
|
||||
// WE EXPECT TO SEE CBZ (for patch 1) INSTRUCTION RIGHT BEFORE FOUND SEQUENCE (requiredInstructionOffsetInternal)
|
||||
// IN RESULTING FILE InstructionOffset SHOULD BE INCREMENTED by 0x100 to get real offset
|
||||
// (because header for decompressed NSO0 size = 0x100; it's fixed alignment produced by libKonogonka)
|
||||
private byte[] getPatch1(int offset) throws Exception{
|
||||
int requiredInstructionOffsetInternal = offset - 4;
|
||||
int requiredInstructionOffsetReal = requiredInstructionOffsetInternal + 0x100;
|
||||
int instructionExpression = Converter.getLEint(_textSection, requiredInstructionOffsetInternal);
|
||||
int patch = ((0x14 << 24) | (instructionExpression >> 5) & 0x7FFFF);
|
||||
|
||||
logPrinter.print(BinToAsmPrinter.printSimplified(patch, requiredInstructionOffsetInternal), EMsgType.NULL);
|
||||
|
||||
// Somehow IPS patches uses offsets written as big_endian (0.o) and bytes dat should be patched as LE.
|
||||
ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN)
|
||||
.putInt(requiredInstructionOffsetReal)
|
||||
.putShort((short) 4)
|
||||
.putInt(Integer.reverseBytes(patch));
|
||||
|
||||
return Arrays.copyOfRange(prePatch.array(), 1, 10);
|
||||
}
|
||||
private byte[] getPatch2(int offset) throws Exception{
|
||||
final int NopExpression = 0x1F2003D5; // reversed
|
||||
int offsetReal = offset - 4 + 0x100;
|
||||
|
||||
logPrinter.print(BinToAsmPrinter.printSimplified(Integer.reverseBytes(NopExpression), offset - 4), EMsgType.NULL);
|
||||
|
||||
ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN)
|
||||
.putInt(offsetReal)
|
||||
.putShort((short) 4)
|
||||
.putInt(NopExpression);
|
||||
|
||||
return Arrays.copyOfRange(prePatch.array(), 1, 10);
|
||||
}
|
||||
private byte[] getPatch3(int offset) throws Exception{
|
||||
int requiredInstructionOffsetInternal = offset - 4;
|
||||
int requiredInstructionOffsetReal = requiredInstructionOffsetInternal + 0x100;
|
||||
|
||||
int instructionExpression = Converter.getLEint(_textSection, requiredInstructionOffsetInternal);
|
||||
int patch = ((0x14 << 24) | (instructionExpression >> 5) & 0x7FFFF);
|
||||
|
||||
logPrinter.print(BinToAsmPrinter.printSimplified(patch, requiredInstructionOffsetInternal), EMsgType.NULL);
|
||||
|
||||
ByteBuffer prePatch = ByteBuffer.allocate(10).order(ByteOrder.BIG_ENDIAN)
|
||||
.putInt(requiredInstructionOffsetReal)
|
||||
.putShort((short) 4)
|
||||
.putInt(Integer.reverseBytes(patch));
|
||||
|
||||
return Arrays.copyOfRange(prePatch.array(), 1, 10);
|
||||
}
|
||||
}
|
187
src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java
Normal file
187
src/main/java/nsusbloader/Utilities/patches/es/EsPatchMaker.java
Normal file
|
@ -0,0 +1,187 @@
|
|||
/*
|
||||
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.es;
|
||||
|
||||
import libKonogonka.KeyChainHolder;
|
||||
import libKonogonka.Tools.NCA.NCAProvider;
|
||||
import nsusbloader.ModelControllers.CancellableRunnable;
|
||||
import nsusbloader.ModelControllers.ILogPrinter;
|
||||
import nsusbloader.ModelControllers.Log;
|
||||
import nsusbloader.NSLDataTypes.EModule;
|
||||
import nsusbloader.NSLDataTypes.EMsgType;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.*;
|
||||
import java.util.concurrent.*;
|
||||
|
||||
public class EsPatchMaker extends CancellableRunnable {
|
||||
private int THREADS_POOL_SIZE = 4;
|
||||
private final ILogPrinter logPrinter;
|
||||
private final String pathToFirmware;
|
||||
private final String pathToKeysFile;
|
||||
private final String saveTo;
|
||||
|
||||
private File firmware;
|
||||
private KeyChainHolder keyChainHolder;
|
||||
private ExecutorService executorService;
|
||||
private List<String> ncaFilesList; // inside the folder
|
||||
|
||||
private boolean oneLinerStatus = false;
|
||||
|
||||
public EsPatchMaker(String pathToFirmware, String pathToKeysFile, String saveTo){
|
||||
this.logPrinter = Log.getPrinter(EModule.PATCHES); //TODO: UNCOMMENT
|
||||
/*
|
||||
this.logPrinter = new ILogPrinter() {
|
||||
@Override
|
||||
public void print(String message, EMsgType type) throws InterruptedException {}
|
||||
@Override
|
||||
public void updateProgress(Double value) throws InterruptedException {}
|
||||
@Override
|
||||
public void update(HashMap<String, File> nspMap, EFileStatus status) {}
|
||||
@Override
|
||||
public void update(File file, EFileStatus status) {}
|
||||
@Override
|
||||
public void updateOneLinerStatus(boolean status) {}
|
||||
@Override
|
||||
public void close() {}
|
||||
};
|
||||
*/
|
||||
this.pathToFirmware = pathToFirmware;
|
||||
this.pathToKeysFile = pathToKeysFile;
|
||||
this.saveTo = saveTo;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
try {
|
||||
logPrinter.print("..:: Make ES Patches ::..", EMsgType.INFO);
|
||||
receiveFirmware();
|
||||
buildKeyChainHolder();
|
||||
receiveNcaFileNamesList();
|
||||
adjustThreadsPoolSize();
|
||||
createPool();
|
||||
executePool();
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
try{
|
||||
logPrinter.print(e.getMessage(), EMsgType.FAIL);
|
||||
} catch (Exception ignore){}
|
||||
}
|
||||
finally {
|
||||
logPrinter.updateOneLinerStatus(oneLinerStatus);
|
||||
logPrinter.close();
|
||||
}
|
||||
}
|
||||
private void receiveFirmware() throws Exception{
|
||||
logPrinter.print("Looking at firmware", EMsgType.INFO);
|
||||
this.firmware = new File(pathToFirmware);
|
||||
}
|
||||
private void buildKeyChainHolder() throws Exception{
|
||||
logPrinter.print("Reading keys", EMsgType.INFO);
|
||||
this.keyChainHolder = new KeyChainHolder(pathToKeysFile, null);
|
||||
}
|
||||
private void receiveNcaFileNamesList() throws Exception{
|
||||
logPrinter.print("Collecting NCA files", EMsgType.INFO);
|
||||
String[] fileNamesArray = firmware.list((File directory, String file) -> ( ! file.endsWith(".cnmt.nca") && file.endsWith(".nca")));
|
||||
ncaFilesList = Arrays.asList(Objects.requireNonNull(fileNamesArray));
|
||||
if (ncaFilesList.size() == 0)
|
||||
throw new Exception("No NCA files found in firmware folder");
|
||||
}
|
||||
private void adjustThreadsPoolSize(){
|
||||
if (ncaFilesList.size() < 4)
|
||||
THREADS_POOL_SIZE = ncaFilesList.size();
|
||||
}
|
||||
|
||||
private void createPool() throws Exception{
|
||||
logPrinter.print("Creating sub-tasks pool", EMsgType.INFO);
|
||||
this.executorService = Executors.newFixedThreadPool(
|
||||
THREADS_POOL_SIZE,
|
||||
runnable -> {
|
||||
Thread thread = new Thread(runnable);
|
||||
thread.setDaemon(true);
|
||||
return thread;
|
||||
});
|
||||
}
|
||||
|
||||
private void executePool() throws Exception{ //TODO: FIX. Exceptions thrown only by logPrinter
|
||||
try {
|
||||
logPrinter.print("Executing sub-tasks pool", EMsgType.INFO);
|
||||
List<Future<NCAProvider>> futuresResults = executorService.invokeAll(getSubTasksCollection());
|
||||
for (Future<NCAProvider> future : futuresResults){
|
||||
NCAProvider ncaProvider = future.get();
|
||||
if (ncaProvider != null) {
|
||||
makePatches(ncaProvider);
|
||||
break;
|
||||
}
|
||||
}
|
||||
executorService.shutdown();
|
||||
}
|
||||
catch (InterruptedException ie){
|
||||
executorService.shutdownNow();
|
||||
boolean interruptedSuccessfully = false;
|
||||
try {
|
||||
interruptedSuccessfully = executorService.awaitTermination(20, TimeUnit.SECONDS);
|
||||
}
|
||||
catch (InterruptedException awaitInterrupt){
|
||||
logPrinter.print("Force interrupting task...", EMsgType.WARNING);
|
||||
}
|
||||
logPrinter.print("Task interrupted "+(interruptedSuccessfully?"successfully":"with some issues"), EMsgType.WARNING);
|
||||
}
|
||||
catch (Exception e){
|
||||
e.printStackTrace();
|
||||
logPrinter.print("Task failed: "+e.getMessage(), EMsgType.FAIL);
|
||||
}
|
||||
}
|
||||
|
||||
private void makePatches(NCAProvider ncaProvider) throws Exception{
|
||||
logPrinter.print(String.format("File found: .."+File.separator+"%s"+File.separator+"%s",
|
||||
ncaProvider.getFile().getParentFile().getName(), ncaProvider.getFile().getName())
|
||||
, EMsgType.INFO);
|
||||
new EsPatch(ncaProvider, saveTo, logPrinter);
|
||||
oneLinerStatus = true;
|
||||
}
|
||||
private List<Callable<NCAProvider>> getSubTasksCollection() throws Exception{
|
||||
logPrinter.print("Forming sub-tasks collection", EMsgType.INFO);
|
||||
List<Callable<NCAProvider>> subTasks = new ArrayList<>();
|
||||
|
||||
int ncaPerThreadAmount = ncaFilesList.size() / THREADS_POOL_SIZE;
|
||||
Iterator<String> iterator = ncaFilesList.listIterator();
|
||||
|
||||
for (int i = 1; i < THREADS_POOL_SIZE; i++){
|
||||
Callable<NCAProvider> task = new EsNcaSearchTask(getNextSet(iterator, ncaPerThreadAmount));
|
||||
subTasks.add(task);
|
||||
}
|
||||
|
||||
Callable<NCAProvider> task = new EsNcaSearchTask(getNextSet(iterator,
|
||||
ncaFilesList.size() % THREADS_POOL_SIZE == 0 ? ncaPerThreadAmount : ncaPerThreadAmount+1));
|
||||
subTasks.add(task);
|
||||
return subTasks;
|
||||
}
|
||||
private List<NCAProvider> getNextSet(Iterator<String> iterator, int amount) throws Exception{
|
||||
List<NCAProvider> ncas = new ArrayList<>();
|
||||
for (int j = 0; j < amount; j++){
|
||||
String ncaFileName = iterator.next();
|
||||
File nca = new File(firmware.getAbsolutePath()+File.separator+ncaFileName);
|
||||
NCAProvider provider = new NCAProvider(nca, keyChainHolder.getRawKeySet());
|
||||
ncas.add(provider);
|
||||
}
|
||||
return ncas;
|
||||
}
|
||||
}
|
161
src/main/java/nsusbloader/Utilities/patches/es/SimplyFind.java
Normal file
161
src/main/java/nsusbloader/Utilities/patches/es/SimplyFind.java
Normal file
|
@ -0,0 +1,161 @@
|
|||
/*
|
||||
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.es;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class SimplyFind {
|
||||
private String what;
|
||||
private final byte[] where;
|
||||
|
||||
private Matcher matcherHex;
|
||||
private Matcher matcherDot;
|
||||
|
||||
private final List<Integer> findings = new ArrayList<>();
|
||||
private final int statementLength;
|
||||
private final List<SearchBlock> searchBlocks = new ArrayList<>();
|
||||
/**
|
||||
* Abstraction layer for searching patterns like "..CAFE..BE" in bytes array.
|
||||
* It's 'String' combination of hex values and '.' which stands for unknown value.
|
||||
* Returns offset of the first symbol.
|
||||
* */
|
||||
public SimplyFind(String what, byte[] where){
|
||||
this.where = where;
|
||||
if (! what.contains(".")){
|
||||
doKMPSearch(Converter.hexStringToByteArray(what), 0);
|
||||
this.statementLength = what.length()/2;
|
||||
return;
|
||||
}
|
||||
this.what = what.replaceAll("\\.", "\\.\\.");
|
||||
this.statementLength = this.what.length()/2;
|
||||
|
||||
buildSearchingSequence();
|
||||
complexSearch();
|
||||
}
|
||||
private void buildSearchingSequence(){
|
||||
Pattern patternHex = Pattern.compile("[0-9]|[A-F]|[a-f]");
|
||||
Pattern patternDot = Pattern.compile("\\.");
|
||||
this.matcherHex = patternHex.matcher(what);
|
||||
this.matcherDot = patternDot.matcher(what);
|
||||
|
||||
int nextDotPos = 0;
|
||||
int nextHexPos;
|
||||
|
||||
while(true){
|
||||
nextHexPos = getNextNumberPosition(nextDotPos);
|
||||
if (nextHexPos == -1)
|
||||
break;
|
||||
|
||||
nextDotPos = getNextDotPosition(nextHexPos);
|
||||
if (nextDotPos == -1) {
|
||||
searchBlocks.add(new SearchBlock(what.substring(nextHexPos), nextHexPos));
|
||||
break;
|
||||
}
|
||||
String searchStatement = what.substring(nextHexPos, nextDotPos);
|
||||
searchBlocks.add(new SearchBlock(searchStatement, nextHexPos));
|
||||
}
|
||||
}
|
||||
private int getNextNumberPosition(int since){
|
||||
if (matcherHex.find(since))
|
||||
return matcherHex.start();
|
||||
return -1;
|
||||
}
|
||||
|
||||
private int getNextDotPosition(int since){
|
||||
if (matcherDot.find(since))
|
||||
return matcherDot.start();
|
||||
return -1;
|
||||
}
|
||||
|
||||
private void complexSearch(){
|
||||
SearchBlock block = searchBlocks.get(0);
|
||||
doKMPSearch(block.statement, block.offsetInStatement);
|
||||
findings.removeIf(this::searchForward);
|
||||
}
|
||||
private boolean searchForward(int offset){
|
||||
for (int i = 1; i < searchBlocks.size(); i++) {
|
||||
SearchBlock block = searchBlocks.get(i);
|
||||
if (! doDumbSearch(block.statement, offset+block.offsetInStatement)){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private void doKMPSearch(byte[] subject, int skip){
|
||||
int whereSize = where.length;
|
||||
int subjectSize = subject.length;
|
||||
|
||||
int[] pf = new int[subjectSize];
|
||||
|
||||
int j = 0;
|
||||
for (int i = 1; i < subjectSize; i++ ) {
|
||||
while ((j > 0) && (subject[j] != subject[i]))
|
||||
j = pf[j-1];
|
||||
if (subject[j] == subject[i])
|
||||
j++;
|
||||
pf[i] = j;
|
||||
}
|
||||
|
||||
j = 0;
|
||||
for (int i = 0; i < whereSize; i++){
|
||||
while ((j > 0) && (subject[j] != where[i]))
|
||||
j = pf[j - 1];
|
||||
if (subject[j] == where[i])
|
||||
j++;
|
||||
if (j == subjectSize) {
|
||||
findings.add(i-j+1-skip);
|
||||
j = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private boolean doDumbSearch(byte[] subject, int since){
|
||||
for (int i = 0; i < subject.length; i++) {
|
||||
if (where[since + i] != subject[i])
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public int getStatementLength() {
|
||||
return statementLength;
|
||||
}
|
||||
|
||||
public List<Integer> getResults(){
|
||||
return findings;
|
||||
}
|
||||
|
||||
private static class SearchBlock{
|
||||
byte[] statement;
|
||||
int offsetInStatement;
|
||||
|
||||
SearchBlock(String statement, int offset){
|
||||
if (statement != null) {
|
||||
this.statement = Converter.hexStringToByteArray(statement);
|
||||
}
|
||||
this.offsetInStatement = offset/2;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,109 @@
|
|||
/*
|
||||
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.es.finders;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class HeuristicEs1 implements IHeuristicEs {
|
||||
private static final String PATTERN = "1F90013128.8052";
|
||||
|
||||
private final List<Integer> findings;
|
||||
private final byte[] where;
|
||||
|
||||
HeuristicEs1(byte[] where){
|
||||
this.where = where;
|
||||
SimplyFind simplyfind = new SimplyFind(PATTERN, where);
|
||||
this.findings = simplyfind.getResults();
|
||||
|
||||
this.findings.removeIf(this::dropStep1);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep2);
|
||||
}
|
||||
|
||||
// Check ranges
|
||||
private boolean dropStep1(int offsetOfPatternFound){
|
||||
return ((offsetOfPatternFound < 0x10000 || offsetOfPatternFound > 0xffffc));
|
||||
}
|
||||
// Remove non-CBZ
|
||||
private boolean dropStep2(int offsetOfPatternFound){
|
||||
return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFound(){
|
||||
return findings.size() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantLessEntropy(){
|
||||
return findings.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() throws Exception{
|
||||
if(findings.isEmpty())
|
||||
throw new Exception("Nothing found");
|
||||
if (findings.size() > 1)
|
||||
throw new Exception("Too many offsets");
|
||||
return findings.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setOffsetsNearby(int offsetNearby) {
|
||||
findings.removeIf(offset -> {
|
||||
if (offset > offsetNearby)
|
||||
return ! (offset < offsetNearby - 0xffff);
|
||||
return ! (offset > offsetNearby - 0xffff);
|
||||
});
|
||||
return isFound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetails(){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int cbzOffsetInternal = findings.get(0) - 4;
|
||||
int instructionExpression = Converter.getLEint(where, cbzOffsetInternal);
|
||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff;
|
||||
|
||||
int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation);
|
||||
int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4);
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(instructionExpression, cbzOffsetInternal));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4),
|
||||
cbzOffsetInternal+4));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8),
|
||||
cbzOffsetInternal+8));
|
||||
builder.append("...\n");
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(){
|
||||
return 1;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,168 @@
|
|||
/*
|
||||
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.es.finders;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
class HeuristicEs2 implements IHeuristicEs {
|
||||
private static final String PATTERN = ".D2.52";
|
||||
|
||||
private List<Integer> findings;
|
||||
private final byte[] where;
|
||||
|
||||
HeuristicEs2(byte[] where){
|
||||
this.where = where;
|
||||
find();
|
||||
|
||||
this.findings.removeIf(this::dropStep1);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep2);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep3);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep4);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep5);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep6);
|
||||
}
|
||||
private void find(){
|
||||
SimplyFind simplyfind = new SimplyFind(PATTERN, where);
|
||||
findings = new ArrayList<>();
|
||||
// This approach a bit different. We're looking for pattern we want to patch, not the next one.
|
||||
// So easier way is just to shift every value and pretend that nothing happened.
|
||||
for (int offset : simplyfind.getResults())
|
||||
findings.add(offset+4);
|
||||
}
|
||||
|
||||
// Limit range
|
||||
private boolean dropStep1(int offsetOfPatternFound){
|
||||
return ((offsetOfPatternFound < 0x10000 || offsetOfPatternFound > 0xffffc));
|
||||
}
|
||||
// Is CBNZ next?
|
||||
private boolean dropStep2(int offsetOfPatternFound){
|
||||
return ! isCBNZ(Converter.getLEint(where, offsetOfPatternFound));
|
||||
}
|
||||
// Check what's above
|
||||
private boolean dropStep3(int offsetOfPatternFound){
|
||||
return ! isMOV(Converter.getLEint(where, offsetOfPatternFound-4));
|
||||
}
|
||||
// Check what's beyond or after jump
|
||||
private boolean dropStep4(int offsetOfPatternFound) {
|
||||
int nextExpression = Converter.getLEint(where, offsetOfPatternFound + 4);
|
||||
return ! isLDRB_LDURB(nextExpression); // Drop if not LDRB OR LDURB
|
||||
}
|
||||
|
||||
// Check second after jump if LDR-TBZ
|
||||
private boolean dropStep5(int offsetOfPatternFound) {
|
||||
int expression = Converter.getLEint(where, offsetOfPatternFound);
|
||||
int afterJumpPosition = ((expression >> 5 & 0x7FFFF) * 4 + offsetOfPatternFound) & 0xfffff;
|
||||
int secondAfterJumpExpression = Converter.getLEint(where, afterJumpPosition+4);
|
||||
return ! isBL(secondAfterJumpExpression); //Second after jump = BL? No -> Drop
|
||||
}
|
||||
|
||||
// Check second after jump if LDR-TBZ
|
||||
private boolean dropStep6(int offsetOfPatternFound) {
|
||||
int expression = Converter.getLEint(where, offsetOfPatternFound);
|
||||
int afterJumpPosition = ((expression >> 5 & 0x7FFFF) * 4 + offsetOfPatternFound) & 0xfffff;
|
||||
int forthAfterJumpExpression = Converter.getLEint(where, afterJumpPosition+12);
|
||||
return ! isBL(forthAfterJumpExpression); //Forth after jump = BL? No -> Drop
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFound(){
|
||||
return findings.size() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantLessEntropy(){
|
||||
return findings.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() throws Exception{
|
||||
if(findings.isEmpty())
|
||||
throw new Exception("Nothing found");
|
||||
if (findings.size() > 1)
|
||||
throw new Exception("Too many offsets");
|
||||
return findings.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setOffsetsNearby(int offsetNearby) {
|
||||
findings.removeIf(offset -> {
|
||||
if (offset > offsetNearby)
|
||||
return ! (offset < offsetNearby - 0xffff);
|
||||
return ! (offset > offsetNearby - 0xffff);
|
||||
});
|
||||
return isFound();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getDetails(){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int secondExpressionOffset = findings.get(0);
|
||||
|
||||
int firstExpression = Converter.getLEint(where, secondExpressionOffset-4);
|
||||
int secondExpression = Converter.getLEint(where, secondExpressionOffset);
|
||||
int conditionalJumpLocation = 0;
|
||||
if ((secondExpression >> 24 & 0x7f) == 0x35) {
|
||||
conditionalJumpLocation = ((secondExpression >> 5 & 0x7FFFF) * 4 + secondExpressionOffset) & 0xfffff;
|
||||
}
|
||||
else if ((firstExpression >> 24 & 0x7f) == 0x36) {
|
||||
conditionalJumpLocation = (secondExpressionOffset-4 + (firstExpression >> 5 & 0x3fff) * 4) & 0xfffff;
|
||||
}
|
||||
int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation);
|
||||
int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation + 4);
|
||||
int secondExpressionsPairElement3 = Converter.getLEint(where, conditionalJumpLocation + 8);
|
||||
int secondExpressionsPairElement4 = Converter.getLEint(where, conditionalJumpLocation + 12);
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, secondExpressionOffset-4), secondExpressionOffset-4));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpression, secondExpressionOffset));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, secondExpressionOffset+4), secondExpressionOffset+4));
|
||||
builder.append("...\n");
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation + 4));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement3, conditionalJumpLocation + 8));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement4, conditionalJumpLocation + 12));
|
||||
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(){
|
||||
return 2;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,151 @@
|
|||
/*
|
||||
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.es.finders;
|
||||
|
||||
import libKonogonka.Converter;
|
||||
import nsusbloader.Utilities.patches.es.BinToAsmPrinter;
|
||||
import nsusbloader.Utilities.patches.es.SimplyFind;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
class HeuristicEs3 implements IHeuristicEs {
|
||||
private static final String PATTERN0 = "..FF97";
|
||||
private static final String PATTERN1 = "......FF97"; // aka "E0230091..FF97";
|
||||
|
||||
private final List<Integer> findings;
|
||||
private final byte[] where;
|
||||
|
||||
HeuristicEs3(long fwVersion, byte[] where){
|
||||
this.where = where;
|
||||
String pattern = getPattern(fwVersion);
|
||||
SimplyFind simplyfind = new SimplyFind(pattern, where);
|
||||
this.findings = simplyfind.getResults();
|
||||
|
||||
this.findings.removeIf(this::dropStep1);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep2);
|
||||
if(findings.size() < 2)
|
||||
return;
|
||||
|
||||
this.findings.removeIf(this::dropStep3);
|
||||
}
|
||||
private String getPattern(long fwVersion){
|
||||
if (fwVersion < 10400)
|
||||
return PATTERN0;
|
||||
return PATTERN1;
|
||||
}
|
||||
// Let's focus on CBZ-ONLY statements
|
||||
private boolean dropStep1(int offsetOfPatternFound){
|
||||
return ((where[offsetOfPatternFound - 1] & (byte) 0b01111111) != 0x34);
|
||||
}
|
||||
|
||||
private boolean dropStep2(int offsetOfPatternFound){
|
||||
int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound - 4);
|
||||
|
||||
int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation);
|
||||
int afterJumpThirdExpressions = Converter.getLEint(where, conditionalJumpLocation+4);
|
||||
// Check first is 'MOV'; second is 'B'
|
||||
return (! isMOV_REG(afterJumpSecondExpressions)) || ! isB(afterJumpThirdExpressions);
|
||||
}
|
||||
|
||||
private boolean dropStep3(int offsetOfPatternFound){
|
||||
int conditionalJumpLocation = getCBZConditionalJumpLocation(offsetOfPatternFound-4);
|
||||
int afterJumpSecondExpressions = Converter.getLEint(where, conditionalJumpLocation+4);
|
||||
int secondPairConditionalJumpLocation = ((afterJumpSecondExpressions & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff;
|
||||
|
||||
int thirdExpressionsPairElement1 = Converter.getLEint(where, secondPairConditionalJumpLocation);
|
||||
int thirdExpressionsPairElement2 = Converter.getLEint(where, secondPairConditionalJumpLocation+4);
|
||||
// Check first is 'ADD'; second is 'BL'
|
||||
return (! isADD(thirdExpressionsPairElement1)) || (! isBL(thirdExpressionsPairElement2));
|
||||
}
|
||||
|
||||
private int getCBZConditionalJumpLocation(int cbzOffsetInternal){
|
||||
int cbzExpression = Converter.getLEint(where, cbzOffsetInternal);
|
||||
return ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isFound(){
|
||||
return findings.size() == 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean wantLessEntropy(){
|
||||
return findings.size() > 1;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOffset() throws Exception{
|
||||
if(findings.isEmpty())
|
||||
throw new Exception("Nothing found");
|
||||
if (findings.size() > 1)
|
||||
throw new Exception("Too many offsets");
|
||||
return findings.get(0);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean setOffsetsNearby(int offsetNearby) {
|
||||
findings.removeIf(offset -> {
|
||||
if (offset > offsetNearby)
|
||||
return ! (offset < offsetNearby - 0xffff);
|
||||
return ! (offset > offsetNearby - 0xffff);
|
||||
});
|
||||
return isFound();
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public String getDetails(){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
int cbzOffsetInternal = findings.get(0) - 4;
|
||||
int cbzExpression = Converter.getLEint(where, cbzOffsetInternal);
|
||||
int conditionalJumpLocation = ((cbzExpression >> 5 & 0x7FFFF) * 4 + cbzOffsetInternal) & 0xfffff;
|
||||
|
||||
int secondExpressionsPairElement1 = Converter.getLEint(where, conditionalJumpLocation);
|
||||
int secondExpressionsPairElement2 = Converter.getLEint(where, conditionalJumpLocation+4);
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(cbzExpression, cbzOffsetInternal));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+4), cbzOffsetInternal+4));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, cbzOffsetInternal+8), cbzOffsetInternal+8));
|
||||
builder.append("...\n");
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement1, conditionalJumpLocation));
|
||||
builder.append(BinToAsmPrinter.printSimplified(secondExpressionsPairElement2, conditionalJumpLocation+4));
|
||||
|
||||
if (((secondExpressionsPairElement2 >> 26 & 0b111111) == 0x5)){
|
||||
builder.append("...\n");
|
||||
int conditionalJumpLocation2 = ((secondExpressionsPairElement2 & 0x3ffffff) * 4 + (conditionalJumpLocation+4)) & 0xfffff;
|
||||
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2), conditionalJumpLocation2));
|
||||
builder.append(BinToAsmPrinter.printSimplified(Converter.getLEint(where, conditionalJumpLocation2+4), conditionalJumpLocation2+4));
|
||||
|
||||
}
|
||||
else {
|
||||
builder.append("NO CONDITIONAL JUMP ON 2nd iteration (HeuristicEs3)");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getId(){
|
||||
return 3;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,127 @@
|
|||
/*
|
||||
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.es.finders;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class HeuristicEsWizard {
|
||||
private final List<IHeuristicEs> all;
|
||||
private final List<IHeuristicEs> found;
|
||||
private final List<IHeuristicEs> wantLessEntropy;
|
||||
|
||||
private final StringBuilder errorsAndNotes;
|
||||
|
||||
private int offset1 = -1;
|
||||
private int offset2 = -1;
|
||||
private int offset3 = -1;
|
||||
|
||||
public HeuristicEsWizard(long fwVersion, byte[] where) throws Exception{
|
||||
this.errorsAndNotes = new StringBuilder();
|
||||
|
||||
this.all = Arrays.asList(
|
||||
new HeuristicEs1(where),
|
||||
new HeuristicEs2(where),
|
||||
new HeuristicEs3(fwVersion, where)
|
||||
);
|
||||
|
||||
this.found = all.stream()
|
||||
.filter(IHeuristicEs::isFound)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
if (found.isEmpty())
|
||||
throw new Exception("Nothing found!");
|
||||
|
||||
this.wantLessEntropy = all.stream()
|
||||
.filter(IHeuristicEs::wantLessEntropy)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
shareOffsetsWithEachOther();
|
||||
|
||||
assignOffset1();
|
||||
assignOffset2();
|
||||
assignOffset3();
|
||||
}
|
||||
|
||||
private void shareOffsetsWithEachOther(){
|
||||
for (IHeuristicEs es : wantLessEntropy) {
|
||||
if (shareWithNext(es))
|
||||
return;
|
||||
}
|
||||
}
|
||||
private boolean shareWithNext(IHeuristicEs es){
|
||||
try {
|
||||
for (IHeuristicEs foundEs : found) {
|
||||
if (es.setOffsetsNearby(foundEs.getOffset())) {
|
||||
found.add(es);
|
||||
wantLessEntropy.remove(es);
|
||||
shareOffsetsWithEachOther();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e){ e.printStackTrace(); }
|
||||
return false;
|
||||
}
|
||||
|
||||
private void assignOffset1(){
|
||||
try {
|
||||
offset1 = all.get(0).getOffset();
|
||||
}
|
||||
catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); }
|
||||
}
|
||||
private void assignOffset2(){
|
||||
try {
|
||||
offset2 = all.get(1).getOffset();
|
||||
}
|
||||
catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); }
|
||||
}
|
||||
private void assignOffset3(){
|
||||
try {
|
||||
offset3 = all.get(2).getOffset();
|
||||
}
|
||||
catch (Exception e){ errorsAndNotes.append(e.getLocalizedMessage()).append("\n"); }
|
||||
}
|
||||
|
||||
public String getErrorsAndNotes(){
|
||||
return errorsAndNotes.toString();
|
||||
}
|
||||
|
||||
public String getDebug(){
|
||||
StringBuilder builder = new StringBuilder();
|
||||
if (all.get(0).isFound()){
|
||||
builder.append("\t\t-=== 1 ===-\n");
|
||||
builder.append(all.get(0).getDetails());
|
||||
}
|
||||
if (all.get(1).isFound()){
|
||||
builder.append("\t\t-=== 2 ===-\n");
|
||||
builder.append(all.get(1).getDetails());
|
||||
}
|
||||
if (all.get(2).isFound()){
|
||||
builder.append("\t\t-=== 3 ===-\n");
|
||||
builder.append(all.get(2).getDetails());
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public int getOffset1() { return offset1; }
|
||||
public int getOffset2() { return offset2; }
|
||||
public int getOffset3() { return offset3; }
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
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.es.finders;
|
||||
/**
|
||||
* Searches instructions (via known patterns) that follows 'specific instruction' we want to patch.
|
||||
* Returns offset of the pattern. Not offset of the 'specific instruction'.
|
||||
* */
|
||||
interface IHeuristicEs {
|
||||
default boolean isLDR(int expression){ return (expression >> 22 & 0x2FF) == 0x2e5; }// LDR ! Sounds like LDP, don't mess up
|
||||
default boolean isLDP(int expression){ return (expression >> 22 & 0x1F9) == 0xA1; }// LDP !
|
||||
default boolean isCBNZ(int expression){ return (expression >> 24 & 0x7f) == 0x35; }
|
||||
default boolean isMOV(int expression){ return (expression >> 23 & 0xff) == 0xA5; }
|
||||
default boolean isTBZ(int expression){ return (expression >> 24 & 0x7f) == 0x36; }
|
||||
default boolean isLDRB_LDURB(int expression){ return (expression >> 21 & 0x7f7) == 0x1c2; }
|
||||
default boolean isMOV_REG(int expression){ return (expression & 0x7FE0FFE0) == 0x2A0003E0; }
|
||||
default boolean isB(int expression) { return (expression >> 26 & 0x3f) == 0x5; }
|
||||
default boolean isBL(int expression){ return (expression >> 26 & 0x3f) == 0x25; }
|
||||
default boolean isADD(int expression){ return (expression >> 23 & 0xff) == 0x22; }
|
||||
boolean isFound();
|
||||
boolean wantLessEntropy();
|
||||
int getOffset() throws Exception;
|
||||
String getDetails();
|
||||
|
||||
/**
|
||||
* Should be used if wantLessEntropy() == true
|
||||
* @return isFound();
|
||||
* */
|
||||
boolean setOffsetsNearby(int offsetNearby);
|
||||
|
||||
int getId();
|
||||
}
|
|
@ -18,9 +18,12 @@
|
|||
*/
|
||||
package nsusbloader.cli;
|
||||
|
||||
import nsusbloader.AppPreferences;
|
||||
import nsusbloader.Main;
|
||||
import nsusbloader.NSLMain;
|
||||
import org.apache.commons.cli.*;
|
||||
|
||||
import java.time.LocalTime;
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
public class CommandLineInterface {
|
||||
|
@ -32,10 +35,12 @@ public class CommandLineInterface {
|
|||
}
|
||||
|
||||
final Options cliOptions = createCliOptions();
|
||||
final Options cliOptionsToBeExact =
|
||||
createCliOptions().addOption(Option.builder().longOpt("gimmegimmegimme").hasArg(false).build());
|
||||
|
||||
CommandLineParser cliParser = new DefaultParser();
|
||||
try{
|
||||
CommandLine cli = cliParser.parse(cliOptions, args);
|
||||
CommandLine cli = cliParser.parse(cliOptionsToBeExact, args);
|
||||
if (cli.hasOption('v') || cli.hasOption("version")){
|
||||
handleVersion();
|
||||
return;
|
||||
|
@ -75,6 +80,16 @@ public class CommandLineInterface {
|
|||
return;
|
||||
}
|
||||
*/
|
||||
if (cli.hasOption("gimmegimmegimme")){
|
||||
if (LocalTime.now().isBefore(LocalTime.parse("09:00:00"))){
|
||||
AppPreferences.getInstance().give();
|
||||
AppPreferences.getInstance().setLastOpenedTab("PatchesTabHolder");
|
||||
System.out.println("=)");
|
||||
Main.main(new String[]{});
|
||||
return;
|
||||
}
|
||||
throw new ParseException("Unhandled LocalTime() exception;");
|
||||
}
|
||||
if (cli.hasOption("s") || cli.hasOption("split")){
|
||||
final String[] arguments = cli.getOptionValues("split");
|
||||
new SplitCli(arguments);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue