diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
index a06f84e..f48f330 100644
--- a/.github/workflows/maven.yml
+++ b/.github/workflows/maven.yml
@@ -13,6 +13,7 @@ on:
branches: [ "workflow" ]
pull_request:
branches: [ "workflow" ]
+ workflow_dispatch:
jobs:
build:
diff --git a/.woodpecker/woodpecker.yml b/.woodpecker/woodpecker.yml
index 11eea07..5d4730f 100644
--- a/.woodpecker/woodpecker.yml
+++ b/.woodpecker/woodpecker.yml
@@ -1,7 +1,7 @@
steps:
- name: test-standard
when:
- event: [tag, push]
+ event: [tag, push, manual]
image: maven:3-openjdk-17
commands:
- mvn -B -DskipTests clean package
@@ -14,7 +14,7 @@ steps:
- name: make-windows-installer
when:
- event: [tag, push]
+ event: [tag, push, manual]
image: wheatstalk/makensis:3
commands:
- cp target/NS-USBloader.exe misc/windows/NSIS/
@@ -32,7 +32,7 @@ steps:
- name: emerge-legacy-artifact
when:
- event: [tag, push]
+ event: [tag, push, manual]
image: maven:3-openjdk-17
commands:
- . ./.make_legacy
@@ -44,7 +44,7 @@ steps:
- name: make-legacy-windows-installer
when:
- event: [tag, push]
+ event: [tag, push, manual]
image: wheatstalk/makensis:3
commands:
- cp target/NS-USBloader.exe misc/windows/NSIS/
@@ -60,7 +60,7 @@ steps:
- name: emerge-mac-m1-artifact
when:
- event: [tag, push]
+ event: [tag, push, manual]
image: maven:3-openjdk-17
commands:
- . ./.make_m1
diff --git a/README.md b/README.md
index 91f8aa2..ed626c9 100644
--- a/README.md
+++ b/README.md
@@ -60,13 +60,14 @@ Sometimes I add new posts about this project [on my blog page](https://developer
* Japanese by [kuragehime](https://github.com/kuragehimekurara1)
* Ryukyuan languages by [kuragehime](https://github.com/kuragehimekurara1)
* Turkish language by [Erimsaholut](https://github.com/Erimsaholut)
+* Serbian (Latin) translation [BlytheScythe](https://github.com/BlytheScythe)
* Angelo Elias Dalzotto makes packages in AUR
* Phoenix[Msc] provides his shiny Mac M1 for debug
### System requirements
-- JDK 11 for macOS and Linux
+- JDK 17 for macOS and Linux
- libusb, if you have a Mac with Apple Silicon (install via `brew install libusb`)
### Supported Goldleaf versions
@@ -77,7 +78,9 @@ Sometimes I add new posts about this project [on my blog page](https://developer
| v0.6.1 | v0.6 |
| v0.7 - 0.7.3 | v0.7+ |
| v0.8 - 0.9 | v1.0+ |
-| v0.10 | v6.0+ |
+| v0.10 - 1.0.0 | v6.0+ |
+| v1.1.0 | none |
+| v1.1.1 | v7.3+ |
where '+' means 'any next NS-USBloader version'.
@@ -247,7 +250,7 @@ We have this situation because of weird behaviour inside usb4java library used i
If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it.
-Upload somewhere (create PR, use pastebin/google drive/whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add.
+If you're familiar with pull request, go ahead and create it! No worries it you are not. Just upload somewhere (like pastebin/google drive/whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add.
To convert files of any locale to readable format (and vise-versa) you can use this site [https://itpro.cz/juniconv/](https://itpro.cz/juniconv/)
diff --git a/pom.xml b/pom.xml
index 82ee300..750b8a8 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
NS-USBloader
ns-usbloader
- 7.2
+ 7.3
https://redrise.ru
NS multi-tool
@@ -50,7 +50,7 @@
UTF-8
yyyyMMdd.HHmmss
19.0.2.1
- 11
+ 17
@@ -205,7 +205,7 @@
maven-compiler-plugin
3.10.1
- 11
+ 17
@@ -274,7 +274,7 @@
%PWD%/jdk
- 11.0.0
+ 17.0.0
${project.version}.0.0
diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java
index ceb9542..9124a3c 100644
--- a/src/main/java/nsusbloader/AppPreferences.java
+++ b/src/main/java/nsusbloader/AppPreferences.java
@@ -29,7 +29,7 @@ public class AppPreferences {
private final Preferences preferences;
private final Locale locale;
- public static final String[] GOLDLEAF_SUPPORTED_VERSIONS = {"v0.5", "v0.7.x", "v0.8-0.9", "v0.10+"};
+ public static final String[] GOLDLEAF_SUPPORTED_VERSIONS = {"v0.5", "v0.7.x", "v0.8-0.9", "v0.10-1.0.0", "v1.1.1"};
private static final Font DEFAULT_FONT = Font.getDefault();
private AppPreferences(){
diff --git a/src/main/java/nsusbloader/Controllers/GamesController.java b/src/main/java/nsusbloader/Controllers/GamesController.java
index 364cb08..371c55d 100644
--- a/src/main/java/nsusbloader/Controllers/GamesController.java
+++ b/src/main/java/nsusbloader/Controllers/GamesController.java
@@ -395,7 +395,7 @@ public class GamesController implements Initializable, ISubscriber {
// If USB selected
if (isGoldLeaf()){
final SettingsBlockGoldleafController goldleafSettings = settings.getGoldleafSettings();
- usbNetCommunications = new UsbCommunications(nspToUpload, "GoldLeaf" + goldleafSettings.getGlVer(), goldleafSettings.getNSPFileFilterForGL());
+ usbNetCommunications = new UsbCommunications(nspToUpload, "GoldLeaf " + goldleafSettings.getGlVer(), goldleafSettings.getNSPFileFilterForGL());
}
else {
if (getSelectedNetUsb().equals("USB")){
diff --git a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
index 0109258..03ad1c4 100644
--- a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
+++ b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
@@ -352,8 +352,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+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+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
@@ -362,8 +362,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+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+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
@@ -372,8 +372,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
@@ -382,8 +382,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " BL " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
@@ -392,15 +392,15 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
+ offset+0x100, 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);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printTBZSimplified(int instructionExpression, int offset){
@@ -410,8 +410,8 @@ public class BinToAsmPrinter {
int label = offset + (instructionExpression >> 5 & 0x3fff) * 4;
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
}
@@ -419,15 +419,15 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
getBConditionalMarker(instructionExpression & 0xf),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printImTooLazy(String name, int instructionExpression, int offset){
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
- offset, Integer.reverseBytes(instructionExpression), instructionExpression);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printSUBSimplified(int instructionExpression, int offset){
@@ -437,8 +437,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " SUB (imm) " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
@@ -448,8 +448,8 @@ public class BinToAsmPrinter {
int Rd = instructionExpression & 0x1F;
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
sfHw, Rm, sfHw, Rd);
}
@@ -458,8 +458,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
}
@@ -471,8 +471,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+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+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
@@ -482,8 +482,8 @@ public class BinToAsmPrinter {
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,
+ "%06x 7100%06x "+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+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
@@ -494,9 +494,9 @@ public class BinToAsmPrinter {
int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP " + ANSI_GREEN + sf + "%d," +
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP " + ANSI_GREEN + sf + "%d," +
ANSI_BLUE + "0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ") " + ANSI_PURPLE + "LSL #%d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
Rn,
conditionalJumpLocation, (conditionalJumpLocation + 0x100),
LSL);
@@ -527,9 +527,9 @@ public class BinToAsmPrinter {
}
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP (sr) " + ANSI_GREEN + sf + "%d," +
+ "%06x 7100%06x "+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,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
Rn, Rm, imm6);
}
@@ -544,23 +544,23 @@ public class BinToAsmPrinter {
imm = instructionExpression >> 10 & 0x1fff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " AND " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " AND " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
sf + "%d" + ANSI_PURPLE + " # ??? 0b%s " + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm));
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm));
}
private static String printRetSimplified(int instructionExpression, int offset){
int Xn = (instructionExpression >> 5) & 0x1F;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
}
private static String printUnknownSimplified(int instructionExpression, int offset){
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String intAsBinString(int number) {
diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
index cd2b528..121bd2f 100644
--- a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
+++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
@@ -148,7 +148,7 @@ public class FsPatch {
private void findAllOffsets() throws Exception{
this.wizard = new HeuristicFsWizard(_textSection);
String errorsAndNotes = wizard.getErrorsAndNotes();
- if (errorsAndNotes.length() > 0)
+ if (! errorsAndNotes.isEmpty())
logPrinter.print(errorsAndNotes, EMsgType.WARNING);
}
private void mkDirs(){
diff --git a/src/main/java/nsusbloader/cli/GoldLeafCli.java b/src/main/java/nsusbloader/cli/GoldLeafCli.java
index 27f6f65..f7dcfde 100644
--- a/src/main/java/nsusbloader/cli/GoldLeafCli.java
+++ b/src/main/java/nsusbloader/cli/GoldLeafCli.java
@@ -125,7 +125,7 @@ public class GoldLeafCli {
private void runGoldLeafBackend() throws InterruptedException {
Runnable task = new UsbCommunications(filesList,
- "GoldLeaf"+goldLeafVersion,
+ "GoldLeaf "+goldLeafVersion,
filterForNsp);
Thread thread = new Thread(task);
thread.setDaemon(true);
diff --git a/src/main/java/nsusbloader/com/usb/GoldLeaf_010.java b/src/main/java/nsusbloader/com/usb/GoldLeaf_010.java
deleted file mode 100644
index 3f420a0..0000000
--- a/src/main/java/nsusbloader/com/usb/GoldLeaf_010.java
+++ /dev/null
@@ -1,1130 +0,0 @@
-/*
- Copyright 2019-2024 Dmitry Isaenko
-
- This file is part of NS-USBloader.
-
- NS-USBloader is free software: you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation, either version 3 of the License, or
- (at your option) any later version.
-
- NS-USBloader is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with NS-USBloader. If not, see .
-*/
-package nsusbloader.com.usb;
-
-import javafx.application.Platform;
-import javafx.stage.FileChooser;
-import nsusbloader.MediatorControl;
-import nsusbloader.ModelControllers.CancellableRunnable;
-import nsusbloader.ModelControllers.ILogPrinter;
-import nsusbloader.NSLDataTypes.EMsgType;
-import nsusbloader.com.helpers.NSSplitReader;
-import org.usb4java.DeviceHandle;
-import org.usb4java.LibUsb;
-
-import java.io.*;
-import java.nio.ByteBuffer;
-import java.nio.ByteOrder;
-import java.nio.IntBuffer;
-import java.nio.charset.StandardCharsets;
-import java.util.*;
-import java.util.concurrent.CompletableFuture;
-
-/**
- * GoldLeaf 0.8 processing
- */
-class GoldLeaf_010 extends TransferModule {
- private boolean nspFilterForGl;
-
- // CMD
- private final byte[] CMD_GLCO_SUCCESS = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD
- private final byte[] CMD_GLCO_FAILURE = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xAD, (byte) 0xDE}; // used @ writeToUsb_GLCMD TODO: TEST
-
- // System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64
- private final byte[] GL_OBJ_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
- private final byte[] GL_OBJ_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
-
- private String recentPath = null;
- private String[] recentDirs = null;
- private String[] recentFiles = null;
-
- private String[] nspMapKeySetIndexes;
-
- private String openReadFileNameAndPath;
- private RandomAccessFile randAccessFile;
- private NSSplitReader splitReader;
-
- private HashMap writeFilesMap;
- private long virtDriveSize;
- private HashMap splitFileSize;
-
- private final boolean isWindows;
- private final String homePath;
- // For using in CMD_SelectFile with SPEC:/ prefix
- private File selectedFile;
-
- private final CancellableRunnable task;
-
- GoldLeaf_010(DeviceHandle handler,
- LinkedHashMap nspMap,
- CancellableRunnable task,
- ILogPrinter logPrinter,
- boolean nspFilter)
- {
- super(handler, nspMap, task, logPrinter);
-
- this.task = task;
-
- final byte CMD_GetDriveCount = 1;
- final byte CMD_GetDriveInfo = 2;
- final byte CMD_StatPath = 3;
- final byte CMD_GetFileCount = 4;
- final byte CMD_GetFile = 5;
- final byte CMD_GetDirectoryCount = 6;
- final byte CMD_GetDirectory = 7;
- final byte CMD_StartFile = 8; // 1 -open read RAF; 2 open write RAF; 3 open write RAF and seek to EOF (???).
- final byte CMD_ReadFile = 9;
- final byte CMD_WriteFile = 10;
- final byte CMD_EndFile = 11; // 1 - closed read RAF; 2 close write RAF.
- final byte CMD_Create = 12;
- final byte CMD_Delete = 13;
- final byte CMD_Rename = 14;
- final byte CMD_GetSpecialPathCount = 15;
- final byte CMD_GetSpecialPath = 16;
- final byte CMD_SelectFile = 17;
-
- final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49};
-
- this.nspFilterForGl = nspFilter;
-
- print("=========== GoldLeaf v0.10 ===========\n\t" +
- "VIRT:/ equals files added into the application\n\t" +
- "HOME:/ equals "
- +System.getProperty("user.home"), EMsgType.INFO);
-
- // Let's collect file names to the array to simplify our life
- writeFilesMap = new HashMap<>();
- int i = 0;
- nspMapKeySetIndexes = new String[nspMap.size()];
- for (String fileName : nspMap.keySet())
- nspMapKeySetIndexes[i++] = fileName;
-
- isWindows = System.getProperty("os.name").contains("Windows");
-
- homePath = System.getProperty("user.home")+File.separator;
-
- splitFileSize = new HashMap<>();
-
- // Calculate size of VIRT:/ drive
- for (File nspFile : nspMap.values()){
- if (nspFile.isDirectory()) {
- File[] subFiles = nspFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
- long size = 0;
- for (File subFile : subFiles) // Validated by parent class
- size += subFile.length();
- virtDriveSize += size;
- splitFileSize.put(nspFile.getName(), size);
- }
- else
- virtDriveSize += nspFile.length();
- }
-
- // Go parse commands
- byte[] readByte;
- int someLength1,
- someLength2;
- main_loop:
- while (true) { // Till user interrupted process.
- readByte = readGL();
-
- if (readByte == null) // Issue @ readFromUsbGL method
- return;
-
- //RainbowHexDump.hexDumpUTF16LE(readByte); // DEBUG
- //System.out.println("CHOICE: "+readByte[4]); // DEBUG
-
- if (Arrays.equals(Arrays.copyOfRange(readByte, 0,4), CMD_GLCI)) {
- switch (readByte[4]) {
- case CMD_GetDriveCount:
- if (getDriveCount())
- break main_loop;
- break;
- case CMD_GetDriveInfo:
- if (getDriveInfo(arrToIntLE(readByte,8)))
- break main_loop;
- break;
- case CMD_GetSpecialPathCount:
- if (getSpecialPathCount())
- break main_loop;
- break;
- case CMD_GetSpecialPath:
- if (getSpecialPath(arrToIntLE(readByte,8)))
- break main_loop;
- break;
- case CMD_GetDirectoryCount:
- someLength1 = arrToIntLE(readByte, 8);
- if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), true))
- break main_loop;
- break;
- case CMD_GetFileCount:
- someLength1 = arrToIntLE(readByte, 8);
- if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), false))
- break main_loop;
- break;
- case CMD_GetDirectory:
- someLength1 = arrToIntLE(readByte, 8);
- if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
- break main_loop;
- break;
- case CMD_GetFile:
- someLength1 = arrToIntLE(readByte, 8);
- if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
- break main_loop;
- break;
- case CMD_StatPath:
- someLength1 = arrToIntLE(readByte, 8);
- if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
- break main_loop;
- break;
- case CMD_Rename:
- someLength1 = arrToIntLE(readByte, 12);
- someLength2 = arrToIntLE(readByte, 16+someLength1);
- if (rename(new String(readByte, 16, someLength1, StandardCharsets.UTF_8),
- new String(readByte, 16+someLength1+4, someLength2, StandardCharsets.UTF_8)))
- break main_loop;
- break;
- case CMD_Delete:
- someLength1 = arrToIntLE(readByte, 12);
- if (delete(new String(readByte, 16, someLength1, StandardCharsets.UTF_8)))
- break main_loop;
- break;
- case CMD_Create:
- someLength1 = arrToIntLE(readByte, 12);
- if (create(new String(readByte, 16, someLength1, StandardCharsets.UTF_8), readByte[8]))
- break main_loop;
- break;
- case CMD_ReadFile:
- someLength1 = arrToIntLE(readByte, 8);
- if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8),
- arrToLongLE(readByte, 12+someLength1),
- arrToLongLE(readByte, 12+someLength1+8)))
- break main_loop;
- break;
- case CMD_WriteFile:
- someLength1 = arrToIntLE(readByte, 8);
- //if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToLongLE(readByte, 12+someLength1)))
- if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
- break main_loop;
- break;
- case CMD_SelectFile:
- if (selectFile())
- break main_loop;
- break;
- case CMD_StartFile:
- case CMD_EndFile:
- if (startOrEndFile())
- break main_loop;
- break;
- default:
- writeGL_FAIL("GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
- }
- }
- }
- // Close (and flush) all opened streams.
- if (writeFilesMap.size() != 0){
- for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
- try{
- fBufOutStream.close();
- }catch (IOException | NullPointerException ignored){}
- }
- }
- closeOpenedReadFilesGl();
- }
-
- /**
- * Close files opened for read/write
- */
- private void closeOpenedReadFilesGl(){
- if (openReadFileNameAndPath != null){ // Perfect time to close our opened files
- try{
- randAccessFile.close();
- }
- catch (IOException | NullPointerException ignored){}
- try{
- splitReader.close();
- }
- catch (IOException | NullPointerException ignored){}
- openReadFileNameAndPath = null;
- randAccessFile = null;
- splitReader = null;
- }
- }
- /**
- * Handle StartFile & EndFile
- * NOTE: It's something internal for GL and used somehow by GL-PC-app, so just ignore this, at least for v0.8.
- * @return true if failed
- * false if everything is ok
- * */
- private boolean startOrEndFile(){
- if (writeGL_PASS()){
- print("GL Handle 'StartFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- /**
- * Handle GetDriveCount
- * @return true if failed
- * false if everything is ok
- */
- private boolean getDriveCount(){
- // Let's declare 2 drives
- byte[] drivesCnt = intToArrLE(2); //2
- // Write count of drives
- if (writeGL_PASS(drivesCnt)) {
- print("GL Handle 'ListDrives' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- /**
- * Handle GetDriveInfo
- * @return true if failed
- * false if everything is ok
- */
- private boolean getDriveInfo(int driveNo){
- if (driveNo < 0 || driveNo > 1){
- return writeGL_FAIL("GL Handle 'GetDriveInfo' command [no such drive]");
- }
-
- byte[] driveLabel,
- driveLabelLen,
- driveLetter,
- driveLetterLen,
- totalFreeSpace,
- totalSize;
- long totalSizeLong;
-
- // 0 == VIRTUAL DRIVE
- if (driveNo == 0){
- driveLabel = "Virtual".getBytes(StandardCharsets.UTF_8);
- driveLabelLen = intToArrLE(driveLabel.length);
- driveLetter = "VIRT".getBytes(StandardCharsets.UTF_8); // TODO: Consider moving to class field declaration
- driveLetterLen = intToArrLE(driveLetter.length);// since GL 0.7
- totalFreeSpace = new byte[4];
- totalSizeLong = virtDriveSize;
- }
- else { //1 == User home dir
- driveLabel = "Home".getBytes(StandardCharsets.UTF_8);
- driveLabelLen = intToArrLE(driveLabel.length);// since GL 0.7
- driveLetter = "HOME".getBytes(StandardCharsets.UTF_8);
- driveLetterLen = intToArrLE(driveLetter.length);// since GL 0.7
- File userHomeDir = new File(System.getProperty("user.home"));
- long totalFreeSpaceLong = userHomeDir.getFreeSpace();
- totalFreeSpace = Arrays.copyOfRange(longToArrLE(totalFreeSpaceLong), 0, 4);;
- totalSizeLong = userHomeDir.getTotalSpace();
- }
- totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4);
-
- List command = new LinkedList<>();
- command.add(driveLabelLen);
- command.add(driveLabel);
- command.add(driveLetterLen);
- command.add(driveLetter);
- command.add(totalFreeSpace);
- command.add(totalSize);
-
- if (writeGL_PASS(command)) {
- print("GL Handle 'GetDriveInfo' command", EMsgType.FAIL);
- return true;
- }
-
- return false;
- }
- /**
- * Handle SpecialPathCount
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getSpecialPathCount(){
- // Let's declare nothing =)
- byte[] specialPathCnt = intToArrLE(0);
- // Write count of special paths
- if (writeGL_PASS(specialPathCnt)) {
- print("GL Handle 'SpecialPathCount' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- /**
- * Handle SpecialPath
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getSpecialPath(int specialPathNo){
- return writeGL_FAIL("GL Handle 'SpecialPath' command [not supported]");
- }
- /**
- * Handle GetDirectoryCount & GetFileCount
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getDirectoryOrFileCount(String path, boolean isGetDirectoryCount) {
- if (path.equals("VIRT:/")) {
- if (isGetDirectoryCount){
- if (writeGL_PASS()) {
- print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
- return true;
- }
- }
- else {
- if (writeGL_PASS(intToArrLE(nspMap.size()))) {
- print("GL Handle 'GetFileCount' command Count = "+nspMap.size(), EMsgType.FAIL);
- return true;
- }
- }
- }
- else if (path.startsWith("HOME:/")){
- // Let's make it normal path
- path = updateHomePath(path);
- // Open it
- File pathDir = new File(path);
-
- // Make sure it's exists and it's path
- if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
- return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [doesn't exist or not a folder]");
- // Save recent dir path
- this.recentPath = path;
- String[] filesOrDirs;
- // Now collecting every folder or file inside
- if (isGetDirectoryCount){
- filesOrDirs = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (dir.isDirectory() && ! dir.isHidden());
- });
- }
- else {
- if (nspFilterForGl){
- filesOrDirs = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (! dir.isDirectory() && name.toLowerCase().endsWith(".nsp"));
- });
- }
- else {
- filesOrDirs = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (! dir.isDirectory() && (! dir.isHidden()));
- });
- }
- }
- // If somehow there are no folders, let's say 0;
- if (filesOrDirs == null){
- if (writeGL_PASS()) {
- print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- // Sorting is mandatory NOTE: Proxy tail
- Arrays.sort(filesOrDirs, String.CASE_INSENSITIVE_ORDER);
-
- if (isGetDirectoryCount)
- this.recentDirs = filesOrDirs;
- else
- this.recentFiles = filesOrDirs;
- // Otherwise, let's tell how may folders are in there
- if (writeGL_PASS(intToArrLE(filesOrDirs.length))) {
- print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
- return true;
- }
- }
- else if (path.startsWith("SPEC:/")){
- if (isGetDirectoryCount){ // If dir request then 0 dirs
- if (writeGL_PASS()) {
- print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
- return true;
- }
- }
- else if (selectedFile != null){ // Else it's file request, if we have selected then we will report 1.
- if (writeGL_PASS(intToArrLE(1))) {
- print("GL Handle 'GetFileCount' command Count = 1", EMsgType.FAIL);
- return true;
- }
- }
- else
- return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+path);
- }
- else { // If requested drive is not VIRT and not HOME then reply error
- return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+path);
- }
- return false;
- }
- /**
- * Handle GetDirectory
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getDirectory(String dirName, int subDirNo){
- if (dirName.startsWith("HOME:/")) {
- dirName = updateHomePath(dirName);
-
- List command = new LinkedList<>();
-
- if (dirName.equals(recentPath) && recentDirs != null && recentDirs.length != 0){
- byte[] dirNameBytes = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
-
- command.add(intToArrLE(dirNameBytes.length));
- command.add(dirNameBytes);
- }
- else {
- File pathDir = new File(dirName);
- // Make sure it's exists and it's path
- if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
- return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
- this.recentPath = dirName;
- // Now collecting every folder or file inside
- this.recentDirs = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (dir.isDirectory() && ! dir.isHidden()); // TODO: FIX FOR WIN ?
- });
- // Check that we still don't have any fuckups
- if (this.recentDirs != null && this.recentDirs.length > subDirNo){
- Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
- byte[] dirBytesName = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
- command.add(intToArrLE(dirBytesName.length));
- command.add(dirBytesName);
- }
- else
- return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
- }
-
- if (writeGL_PASS(command)) {
- print("GL Handle 'GetDirectory' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- // VIRT:// and any other
- return writeGL_FAIL("GL Handle 'GetDirectory' command for virtual drive [no folders support]");
- }
- /**
- * Handle GetFile
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getFile(String dirName, int subDirNo){
- List command = new LinkedList<>();
-
- if (dirName.startsWith("HOME:/")) {
- dirName = updateHomePath(dirName);
-
- if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
- byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
-
- command.add(intToArrLE(fileNameBytes.length)); //Since GL 0.7
- command.add(fileNameBytes);
- }
- else {
- File pathDir = new File(dirName);
- // Make sure it's exists and it's path
- if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
- writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
- this.recentPath = dirName;
- // Now collecting every folder or file inside
- if (nspFilterForGl){
- this.recentFiles = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (! dir.isDirectory() && name.toLowerCase().endsWith(".nsp")); // TODO: FIX FOR WIN ? MOVE TO PROD
- });
- }
- else {
- this.recentFiles = pathDir.list((current, name) -> {
- File dir = new File(current, name);
- return (! dir.isDirectory() && (! dir.isHidden())); // TODO: FIX FOR WIN
- });
- }
- // Check that we still don't have any fuckups
- if (this.recentFiles != null && this.recentFiles.length > subDirNo){
- Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER); // TODO: NOTE: array sorting is an overhead for using poxy loops
- byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
- command.add(intToArrLE(fileNameBytes.length)); //Since GL 0.7
- command.add(fileNameBytes);
- }
- else
- return writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
- }
-
- if (writeGL_PASS(command)) {
- print("GL Handle 'GetFile' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- else if (dirName.equals("VIRT:/")){
- if (nspMap.size() != 0){ // therefore nspMapKeySetIndexes also != 0
- byte[] fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_8);
- command.add(intToArrLE(fileNameBytes.length));
- command.add(fileNameBytes);
- if (writeGL_PASS(command)) {
- print("GL Handle 'GetFile' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- else if (dirName.equals("SPEC:/")){
- if (selectedFile != null){
- byte[] fileNameBytes = selectedFile.getName().getBytes(StandardCharsets.UTF_8);
- command.add(intToArrLE(fileNameBytes.length));
- command.add(fileNameBytes);
- if (writeGL_PASS(command)) {
- print("GL Handle 'GetFile' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- // any other cases
- return writeGL_FAIL("GL Handle 'GetFile' command for virtual drive [no folders support?]");
- }
- /**
- * Handle StatPath
- * @return true if failed
- * false if everything is ok
- * */
- private boolean statPath(String filePath){
- List command = new LinkedList<>();
-
- if (filePath.startsWith("HOME:/")){
- filePath = updateHomePath(filePath);
-
- File fileDirElement = new File(filePath);
- if (fileDirElement.exists()){
- if (fileDirElement.isDirectory())
- command.add(GL_OBJ_TYPE_DIR);
- else {
- command.add(GL_OBJ_TYPE_FILE);
- command.add(longToArrLE(fileDirElement.length()));
- }
- if (writeGL_PASS(command)) {
- print("GL Handle 'StatPath' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- else if (filePath.startsWith("VIRT:/")) {
- filePath = filePath.replaceFirst("VIRT:/", "");
- if (nspMap.containsKey(filePath)){
- command.add(GL_OBJ_TYPE_FILE); // THIS IS INT
- if (nspMap.get(filePath).isDirectory()) {
- command.add(longToArrLE(splitFileSize.get(filePath))); // YES, THIS IS LONG!;
- }
- else
- command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
-
- if (writeGL_PASS(command)) {
- print("GL Handle 'StatPath' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- else if (filePath.startsWith("SPEC:/")){
- //System.out.println(filePath);
- filePath = filePath.replaceFirst("SPEC:/","");
- if (selectedFile.getName().equals(filePath)){
- command.add(GL_OBJ_TYPE_FILE);
- command.add(longToArrLE(selectedFile.length()));
- if (writeGL_PASS(command)) {
- print("GL Handle 'StatPath' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- return writeGL_FAIL("GL Handle 'StatPath' command [no such folder] - "+filePath);
- }
- /**
- * Handle 'Rename' that is actually 'mv'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean rename(String fileName, String newFileName){
- if (fileName.startsWith("HOME:/")){
- // This shit takes too much time to explain, but such behaviour won't let GL to fail
- this.recentPath = null;
- this.recentFiles = null;
- this.recentDirs = null;
- fileName = updateHomePath(fileName);
- newFileName = updateHomePath(newFileName);
-
- File currentFile = new File(fileName);
- File newFile = new File(newFileName);
- if (! newFile.exists()){ // Else, report error
- try {
- if (currentFile.renameTo(newFile)){
- if (writeGL_PASS()) {
- print("GL Handle 'Rename' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- catch (SecurityException ignored){} // Ah, leave it
- }
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Rename' command [not supported for virtual drive/wrong drive/file with such name already exists/read-only directory]");
- }
- /**
- * Handle 'Delete'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean delete(String fileName) {
- if (fileName.startsWith("HOME:/")) {
- fileName = updateHomePath(fileName);
-
- File fileToDel = new File(fileName);
- try {
- if (fileToDel.delete()){
- if (writeGL_PASS()) {
- print("GL Handle 'Rename' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- catch (SecurityException ignored){} // Ah, leave it
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
- }
- /**
- * Handle 'Create'
- * @param type 1 for file
- * 2 for folder
- * @param fileName full path including new file name in the end
- * @return true if failed
- * false if everything is ok
- * */
- private boolean create(String fileName, byte type) {
- if (fileName.startsWith("HOME:/")) {
- fileName = updateHomePath(fileName);
- File fileToCreate = new File(fileName);
- boolean result = false;
- if (type == 1){
- try {
- result = fileToCreate.createNewFile();
- }
- catch (SecurityException | IOException ignored){}
- }
- else if (type == 2){
- try {
- result = fileToCreate.mkdir();
- }
- catch (SecurityException ignored){}
- }
- if (result){
- if (writeGL_PASS()) {
- print("GL Handle 'Create' command.", EMsgType.FAIL);
- return true;
- }
- //print("GL Handle 'Create' command.", EMsgType.PASS);
- return false;
- }
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
- }
-
- /**
- * Handle 'ReadFile'
- * @param fileName full path including new file name in the end
- * @param offset requested offset
- * @param size requested size
- * @return true if failed
- * false if everything is ok
- * */
- private boolean readFile(String fileName, long offset, long size) {
- //System.out.println("readFile "+fileName+"\t"+offset+"\t"+size+"\n");
- if (fileName.startsWith("VIRT:/")){
- // Let's find out which file requested
- String fNamePath = nspMap.get(fileName.substring(6)).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
- // If we don't have this file opened, let's open it
- if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fNamePath))) {
- // Try close what opened
- if (openReadFileNameAndPath != null){
- try{
- randAccessFile.close();
- }catch (Exception ignored){}
- try{
- splitReader.close();
- }catch (Exception ignored){}
- }
- // Open what has to be opened
- try{
- File tempFile = nspMap.get(fileName.substring(6));
- if (tempFile.isDirectory()) {
- randAccessFile = null;
- splitReader = new NSSplitReader(tempFile, 0);
- }
- else {
- splitReader = null;
- randAccessFile = new RandomAccessFile(tempFile, "r");
- }
- openReadFileNameAndPath = fNamePath;
- }
- catch (IOException | NullPointerException ioe){
- return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
- }
- }
- }
- else {
- // Let's find out which file requested
- fileName = updateHomePath(fileName);
- // If we don't have this file opened, let's open it
- if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fileName))) {
- // Try close what opened
- if (openReadFileNameAndPath != null){
- try{
- randAccessFile.close();
- }catch (IOException | NullPointerException ignored){}
- }
- // Open what has to be opened
- try{
- randAccessFile = new RandomAccessFile(fileName, "r");
- openReadFileNameAndPath = fileName;
- }catch (IOException | NullPointerException ioe){
- return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
- }
- }
- }
- //----------------------- Actual transfer chain ------------------------
- try{
- if (randAccessFile == null){
- splitReader.seek(offset);
- byte[] chunk = new byte[(int)size]; // WTF MAN?
- // Let's find out how much bytes we got
- int bytesRead = splitReader.read(chunk);
- // Let's check that we read expected size
- if (bytesRead != (int)size)
- return writeGL_FAIL("GL Handle 'ReadFile' command [CMD]" +
- "\n At offset: " + offset +
- "\n Requested: " + size +
- "\n Received: " + bytesRead);
- // Let's tell as a command about our result.
- if (writeGL_PASS(longToArrLE(size))) {
- print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
- return true;
- }
- // Let's bypass bytes we read total
- if (writeToUsb(chunk)) {
- print("GL Handle 'ReadFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- else {
- randAccessFile.seek(offset);
- byte[] chunk = new byte[(int)size]; // yes, I know, but nothing to do here.
- // Let's find out how much bytes we got
- int bytesRead = randAccessFile.read(chunk);
- // Let's check that we read expected size
- if (bytesRead != (int)size)
- return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
- // Let's tell as a command about our result.
- if (writeGL_PASS(longToArrLE(size))) {
- print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
- return true;
- }
- // Let's bypass bytes we read total
- if (writeToUsb(chunk)) {
- print("GL Handle 'ReadFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- catch (Exception ioe){
- try{
- randAccessFile.close();
- }
- catch (NullPointerException ignored){}
- catch (IOException ioe_){
- print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
- }
- try{
- splitReader.close();
- }
- catch (NullPointerException ignored){}
- catch (IOException ioe_){
- print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
- }
- openReadFileNameAndPath = null;
- randAccessFile = null;
- splitReader = null;
- return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
- }
- }
- /**
- * Handle 'WriteFile'
- * @param fileName full path including new file name in the end
- *
- * @return true if failed
- * false if everything is ok
- * */
- //@param size requested size
- //private boolean writeFile(String fileName, long size) {
- private boolean writeFile(String fileName) {
- if (fileName.startsWith("VIRT:/")){
- return writeGL_FAIL("GL Handle 'WriteFile' command [not supported for virtual drive]");
- }
-
- fileName = updateHomePath(fileName);
- // Check if we didn't see this (or any) file during this session
- if (writeFilesMap.size() == 0 || (! writeFilesMap.containsKey(fileName))){
- // Open what we have to open
- File writeFile = new File(fileName);
- // If this file exists GL will take care
- // Otherwise, let's add it
- try{
- BufferedOutputStream writeFileBufOutStream = new BufferedOutputStream(new FileOutputStream(writeFile, true));
- writeFilesMap.put(fileName, writeFileBufOutStream);
- } catch (IOException ioe){
- return writeGL_FAIL("GL Handle 'WriteFile' command [IOException]\n\t"+ioe.getMessage());
- }
- }
- // Now we have stream
- BufferedOutputStream myStream = writeFilesMap.get(fileName);
-
- byte[] transferredData;
-
- if ((transferredData = readGL_file()) == null){
- print("GL Handle 'WriteFile' command [1/1]", EMsgType.FAIL);
- return true;
- }
- try{
- myStream.write(transferredData, 0, transferredData.length);
- }
- catch (IOException ioe){
- return writeGL_FAIL("GL Handle 'WriteFile' command [1/1]\n\t"+ioe.getMessage());
- }
- // Report we're good
- if (writeGL_PASS()) {
- print("GL Handle 'WriteFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
-
- /**
- * Handle 'SelectFile'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean selectFile(){
- File selectedFile = CompletableFuture.supplyAsync(() -> {
- FileChooser fChooser = new FileChooser();
- fChooser.setTitle(MediatorControl.INSTANCE.getResourceBundle().getString("btn_OpenFile")); // TODO: FIX BAD IMPLEMENTATION
- fChooser.setInitialDirectory(new File(System.getProperty("user.home")));// TODO: Consider fixing; not a priority.
- fChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("*", "*"));
- return fChooser.showOpenDialog(null); // Leave as is for now.
- }, Platform::runLater).join();
-
- if (selectedFile == null){ // Nothing selected
- this.selectedFile = null;
- return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
- }
-
- List command = new LinkedList<>();
- byte[] selectedFileNameBytes = ("SPEC:/"+selectedFile.getName()).getBytes(StandardCharsets.UTF_8);
- command.add(intToArrLE(selectedFileNameBytes.length));
- command.add(selectedFileNameBytes);
- if (writeGL_PASS(command)) {
- print("GL Handle 'SelectFile' command", EMsgType.FAIL);
- this.selectedFile = null;
- return true;
- }
- this.selectedFile = selectedFile;
- return false;
- }
-
- /*----------------------------------------------------*/
- /* GL HELPERS */
- /*----------------------------------------------------*/
- /**
- * Convert path received from GL to normal
- */
- private String updateHomePath(String glPath){
- if (isWindows)
- glPath = glPath.replaceAll("/", "\\\\");
- glPath = homePath+glPath.substring(6); // Do not use replaceAll since it will consider \ as special directive
- return glPath;
- }
- /**
- * Convert INT (Little endian) value to bytes-array representation
- * */
- private byte[] intToArrLE(int value){
- ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.putInt(value);
- return byteBuffer.array();
- }
- /**
- * Convert LONG (Little endian) value to bytes-array representation
- * */
- private byte[] longToArrLE(long value){
- ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.putLong(value);
- return byteBuffer.array();
- }
- /**
- * Convert bytes-array to INT value (Little endian)
- * */
- private int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
- return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
- }
- /**
- * Convert bytes-array to LONG value (Little endian)
- * */
- private long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
- return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
- }
-
- //------------------------------------------------------------------------------------------------------------------
-
- /*----------------------------------------------------*/
- /* GL READ/WRITE USB SPECIFIC */
- /*----------------------------------------------------*/
-
- private byte[] readGL(){
- ByteBuffer readBuffer;
- readBuffer = ByteBuffer.allocateDirect(4096); // GL really?
-
- IntBuffer readBufTransferred = IntBuffer.allocate(1);
-
- int result;
-
- while (! task.isCancelled()) {
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
-
- switch (result) {
- case LibUsb.SUCCESS:
- int trans = readBufTransferred.get();
- byte[] receivedBytes = new byte[trans];
- readBuffer.get(receivedBytes);
- return receivedBytes;
- case LibUsb.ERROR_TIMEOUT:
- closeOpenedReadFilesGl(); // Could be a problem if GL glitches and slow down process. Or if user has extra-slow SD card. TODO: refactor?
- continue;
- default:
- print("GL Data transfer issue [read]\n Returned: " +
- UsbErrorCodes.getErrCode(result) +
- "\n GL Execution stopped", EMsgType.FAIL);
- return null;
- }
- }
- print("GL Execution interrupted", EMsgType.INFO);
- return null;
- }
- private byte[] readGL_file(){
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(8388608); // Just don't ask..
- IntBuffer readBufTransferred = IntBuffer.allocate(1);
-
- int result;
-
- while (! task.isCancelled() ) {
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
-
- switch (result) {
- case LibUsb.SUCCESS:
- int trans = readBufTransferred.get();
- byte[] receivedBytes = new byte[trans];
- readBuffer.get(receivedBytes);
- return receivedBytes;
- case LibUsb.ERROR_TIMEOUT:
- continue;
- default:
- print("GL Data transfer issue [read]\n Returned: " +
- UsbErrorCodes.getErrCode(result) +
- "\n GL Execution stopped", EMsgType.FAIL);
- return null;
- }
- }
- print("GL Execution interrupted", EMsgType.INFO);
- return null;
- }
- /**
- * Write new command. Shitty implementation.
- * */
- private boolean writeGL_PASS(byte[] message){
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(message);
- return writeToUsb(writeBuffer.array());
- }
- private boolean writeGL_PASS(){
- return writeToUsb(Arrays.copyOf(CMD_GLCO_SUCCESS, 4096));
- }
- private boolean writeGL_PASS(List messages){
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- writeBuffer.put(CMD_GLCO_SUCCESS);
- for (byte[] arr : messages)
- writeBuffer.put(arr);
- return writeToUsb(writeBuffer.array());
- }
-
- private boolean writeGL_FAIL(String reportToUImsg){
- if (writeToUsb(Arrays.copyOf(CMD_GLCO_FAILURE, 4096))){
- print(reportToUImsg, EMsgType.WARNING);
- return true;
- }
- print(reportToUImsg, EMsgType.FAIL);
- return false;
- }
- /**
- * Sending any byte array to USB device
- * @return 'false' if no issues
- * 'true' if errors happened
- * */
- private boolean writeToUsb(byte[] message){
- //System.out.println(">");
- //RainbowHexDump.hexDumpUTF16LE(message); // DEBUG
- ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
- writeBuffer.put(message); // Don't do writeBuffer.rewind();
- IntBuffer writeBufTransferred = IntBuffer.allocate(1);
- int result;
-
- while (! task.isCancelled()) {
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
-
- switch (result){
- case LibUsb.SUCCESS:
- if (writeBufTransferred.get() == message.length)
- return false;
- else {
- print("GL Data transfer issue [write]\n Requested: " +
- message.length +
- "\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
- return true;
- }
- case LibUsb.ERROR_TIMEOUT:
- continue;
- default:
- print("GL Data transfer issue [write]\n Returned: " +
- UsbErrorCodes.getErrCode(result) +
- "\n GL Execution stopped", EMsgType.FAIL);
- return true;
- }
- }
- print("GL Execution interrupted", EMsgType.INFO);
- return true;
- }
-}
diff --git a/src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java b/src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java
index e97352e..9491596 100644
--- a/src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java
+++ b/src/main/java/nsusbloader/com/usb/PFS/PFSProvider.java
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 Dmitry Isaenko
+ Copyright 2019-2025 Dmitry Isaenko
This file is part of NS-USBloader.
@@ -33,9 +33,9 @@ import java.util.*;
public class PFSProvider {
private static final byte[] PFS0 = new byte[]{0x50, 0x46, 0x53, 0x30}; // PFS0
- private String nspFileName;
- private NCAFile[] ncaFiles;
- private long bodySize;
+ private final String nspFileName;
+ private final NCAFile[] ncaFiles;
+ private final long bodySize;
private int ticketID = -1;
public PFSProvider(File nspFile, ILogPrinter logPrinter) throws Exception{
@@ -141,7 +141,7 @@ public class PFSProvider {
byte[] b = new byte[1]; // Temporary
for (int i=0; i();
- randAccessFile.seek(filesCount*24+16+ncaNameOffsets.get(i)); // Files cont * 24(bit for each meta-data) + 4 bytes goes after all of them + 12 bit what were in the beginning
+ randAccessFile.seek(filesCount*24L+16L+ncaNameOffsets.get(i)); // Files cont * 24(bit for each meta-data) + 4 bytes goes after all of them + 12 bit what were in the beginning
while ((randAccessFile.read(b)) != -1){
if (b[0] == 0x00)
break;
diff --git a/src/main/java/nsusbloader/com/usb/TransferModule.java b/src/main/java/nsusbloader/com/usb/TransferModule.java
index e99a4a8..9ea4694 100644
--- a/src/main/java/nsusbloader/com/usb/TransferModule.java
+++ b/src/main/java/nsusbloader/com/usb/TransferModule.java
@@ -28,14 +28,20 @@ import java.io.File;
import java.util.*;
public abstract class TransferModule {
- EFileStatus status = EFileStatus.UNKNOWN;
+ protected static final byte IN_EP = (byte) 0x81;
+ protected static final byte OUT_EP = (byte) 0x01;
- LinkedHashMap nspMap;
- ILogPrinter logPrinter;
- DeviceHandle handlerNS;
- CancellableRunnable task;
+ protected EFileStatus status = EFileStatus.UNKNOWN;
- TransferModule(DeviceHandle handler, LinkedHashMap nspMap, CancellableRunnable task, ILogPrinter printer){
+ protected LinkedHashMap nspMap;
+ protected ILogPrinter logPrinter;
+ protected DeviceHandle handlerNS;
+ protected CancellableRunnable task;
+
+ protected TransferModule(DeviceHandle handler,
+ LinkedHashMap nspMap,
+ CancellableRunnable task,
+ ILogPrinter printer){
this.handlerNS = handler;
this.nspMap = nspMap;
this.task = task;
@@ -78,7 +84,7 @@ public abstract class TransferModule {
}
public EFileStatus getStatus(){ return status; }
- void print(String message, EMsgType type){
+ protected void print(String message, EMsgType type){
try {
logPrinter.print(message, type);
}
diff --git a/src/main/java/nsusbloader/com/usb/UsbCommunications.java b/src/main/java/nsusbloader/com/usb/UsbCommunications.java
index 76063d4..55ac2b9 100644
--- a/src/main/java/nsusbloader/com/usb/UsbCommunications.java
+++ b/src/main/java/nsusbloader/com/usb/UsbCommunications.java
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 Dmitry Isaenko
+ Copyright 2019-2025 Dmitry Isaenko
This file is part of NS-USBloader.
@@ -24,6 +24,8 @@ import nsusbloader.ModelControllers.Log;
import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.NSLDataTypes.EModule;
import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.com.usb.gl.GoldLeaf_010;
+import nsusbloader.com.usb.gl.GoldLeaf_111;
import org.usb4java.*;
import java.io.*;
@@ -66,13 +68,16 @@ public class UsbCommunications extends CancellableRunnable {
case "TinFoil":
module = new TinFoil(handler, nspMap, this, logPrinter);
break;
- case "GoldLeafv0.10+":
+ case "GoldLeaf v1.1.1":
+ module = new GoldLeaf_111(handler, nspMap, this, logPrinter, nspFilterForGl);
+ break;
+ case "GoldLeaf v0.10-1.0.0":
module = new GoldLeaf_010(handler, nspMap, this, logPrinter, nspFilterForGl);
break;
- case "GoldLeafv0.8-0.9":
+ case "GoldLeaf v0.8-0.9":
module = new GoldLeaf_08(handler, nspMap, this, logPrinter, nspFilterForGl);
break;
- case "GoldLeafv0.7.x":
+ case "GoldLeaf v0.7.x":
module = new GoldLeaf_07(handler, nspMap, this, logPrinter, nspFilterForGl);
break;
default:
diff --git a/src/main/java/nsusbloader/com/usb/gl/Converters.java b/src/main/java/nsusbloader/com/usb/gl/Converters.java
new file mode 100644
index 0000000..71a6d44
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/Converters.java
@@ -0,0 +1,53 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.com.usb.gl;
+
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+
+public class Converters {
+ /**
+ * Convert INT (Little endian) value to bytes-array representation
+ * */
+ public static byte[] intToArrLE(int value){
+ return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN)
+ .putInt(value)
+ .array();
+ }
+ /**
+ * Convert LONG (Little endian) value to bytes-array representation
+ * */
+ public static byte[] longToArrLE(long value){
+ return ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN)
+ .putLong(value)
+ .array();
+ }
+ /**
+ * Convert bytes-array to INT value (Little endian)
+ * */
+ public static int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
+ }
+ /**
+ * Convert bytes-array to LONG value (Little endian)
+ * */
+ public static long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
+ }
+}
diff --git a/src/main/java/nsusbloader/com/usb/gl/GlString.java b/src/main/java/nsusbloader/com/usb/gl/GlString.java
new file mode 100644
index 0000000..3c90b5c
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/GlString.java
@@ -0,0 +1,23 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.com.usb.gl;
+
+public interface GlString {
+ int length();
+}
diff --git a/src/main/java/nsusbloader/com/usb/gl/GlString010.java b/src/main/java/nsusbloader/com/usb/gl/GlString010.java
new file mode 100644
index 0000000..6546205
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/GlString010.java
@@ -0,0 +1,44 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.com.usb.gl;
+
+import java.nio.charset.StandardCharsets;
+
+import static nsusbloader.com.usb.gl.Converters.arrToIntLE;
+
+/* Separated from interface for easier fixes/replacement in future */
+public class GlString010 implements GlString {
+
+ private final int length;
+ private final String string;
+
+ public GlString010(byte[] inputBytes, int startPosition){
+ this.length = arrToIntLE(inputBytes, startPosition);
+ this.string = new String(inputBytes, startPosition+4, length, StandardCharsets.UTF_8);
+ }
+
+ public int length(){
+ return length;
+ }
+
+ @Override
+ public String toString(){
+ return string;
+ }
+}
diff --git a/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_010.java b/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_010.java
new file mode 100644
index 0000000..e608a00
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_010.java
@@ -0,0 +1,897 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+*/
+package nsusbloader.com.usb.gl;
+
+import javafx.application.Platform;
+import javafx.stage.FileChooser;
+import nsusbloader.MediatorControl;
+import nsusbloader.ModelControllers.CancellableRunnable;
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.com.helpers.NSSplitReader;
+import nsusbloader.com.usb.TransferModule;
+import org.usb4java.DeviceHandle;
+import org.usb4java.LibUsb;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.IntBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+import static nsusbloader.com.usb.gl.Converters.*;
+
+/**
+ * GoldLeaf v0.10 processing
+ */
+public class GoldLeaf_010 extends TransferModule {
+
+ private final static int PACKET_SIZE = 4096;
+ // CMD
+ public final static byte[] EXCEPTION_CAUGHT = Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xF1, (byte) 0xBA}, PACKET_SIZE);
+ public final static byte[] INVALID_INDEX = Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xF2, (byte) 0xBA}, PACKET_SIZE);
+ public final static byte[] SELECTION_CANCELLED = Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xF4, (byte) 0xBA}, PACKET_SIZE);
+
+ private final static byte[] CMD_GLCO_SUCCESS_FLAG = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00};
+
+ private final static byte[] CMD_GLCO_SUCCESS =
+ Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}, PACKET_SIZE);
+
+ // System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64
+ private final byte[] GL_OBJECT_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
+ private final byte[] GL_OBJECT_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
+
+ private final boolean nspFilter;
+
+ private String recentPath;
+ private String[] recentDirs;
+ private String[] recentFiles;
+
+ private final String[] nspMapKeySetIndexes;
+
+ protected String openReadFileNameAndPath;
+ protected RandomAccessFile randAccessFile;
+ protected NSSplitReader splitReader;
+
+ private final HashMap writeFilesMap = new HashMap<>();
+ protected long virtDriveSize;
+ private final HashMap splitFileSize = new HashMap<>();
+
+ private final boolean isWindows = System.getProperty("os.name").contains("Windows");
+ protected final String homePath = System.getProperty("user.home");
+ // For using in CMD_SelectFile with SPEC:/ prefix
+ protected File selectedFile;
+
+ public GoldLeaf_010(DeviceHandle handler,
+ LinkedHashMap nspMap,
+ CancellableRunnable task,
+ ILogPrinter logPrinter,
+ boolean nspFilter) {
+ super(handler, nspMap, task, logPrinter);
+
+ this.nspFilter = nspFilter;
+
+ printWelcomeMessage();
+
+ // Let's collect file names to the array (simplifies flow)
+ nspMapKeySetIndexes = nspMap.keySet().toArray(new String[0]);
+
+ // Calculate size of VIRT:/ drive
+ for (File nspFile : nspMap.values()) {
+ if (nspFile.isDirectory()) {
+ var subFiles = nspFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
+ assert subFiles != null;
+ long size = 0;
+ for (File subFile : subFiles) // Validated by parent class
+ size += subFile.length();
+ virtDriveSize += size;
+ splitFileSize.put(nspFile.getName(), size);
+ }
+ else
+ virtDriveSize += nspFile.length();
+ }
+
+ // Go parse commands
+ if (workLoop())
+ return;
+ // Close (and flush) all opened streams.
+ for (var bufferedOutputStream: writeFilesMap.values()){
+ try{
+ bufferedOutputStream.close();
+ } catch (IOException | NullPointerException ignored){}
+ }
+ closeOpenedReadFilesGl();
+ }
+
+ protected void printWelcomeMessage(){
+ print("=========== GoldLeaf v0.10-1.0.0 ===========\n\t" +
+ "VIRT:/ equals files added into the application\n\t" +
+ "HOME:/ equals " + homePath, EMsgType.INFO);
+ }
+
+ protected boolean workLoop(){
+ while (true) { // Till user interrupted process.
+ GlString glString1;
+ var readByte = readGL();
+
+ if (readByte == null) // Issue @ readFromUsbGL method
+ return true;
+
+ //RainbowDump.hexDumpUTF8(readByte); // DEBUG
+ //print("\tâ "+ GoldleafCmd.get(readByte[4]), EMsgType.INFO);
+
+ if (notGLCI(readByte))
+ continue;
+
+ switch (GoldleafCmd.get(readByte[4])) {
+ case GetDriveCount:
+ if (getDriveCount())
+ return false;
+ break;
+ case GetDriveInfo:
+ if (getDriveInfo(arrToIntLE(readByte,8)))
+ return false;
+ break;
+ case GetSpecialPathCount:
+ if (getSpecialPathCount())
+ return false;
+ break;
+ case GetSpecialPath:
+ if (getSpecialPath(arrToIntLE(readByte,8)))
+ return false;
+ break;
+ case GetDirectoryCount:
+ if (getDirectoryOrFileCount(readString(readByte, 8).toString(), true))
+ return false;
+ break;
+ case GetFileCount:
+ if (getDirectoryOrFileCount(readString(readByte, 8).toString(), false))
+ return false;
+ break;
+ case GetDirectory:
+ glString1 = readString(readByte, 8);
+ if (getDirectory(glString1.toString(), arrToIntLE(readByte, glString1.length()+12)))
+ return false;
+ break;
+ case GetFile:
+ glString1 = readString(readByte, 8);
+ if (getFile(glString1.toString(), arrToIntLE(readByte, glString1.length()+12)))
+ return false;
+ break;
+ case StatPath:
+ if (statPath(readString(readByte, 8).toString()))
+ return false;
+ break;
+ case Rename:
+ glString1 = readString(readByte, 8);
+ var glString2 = readString(readByte, 12+glString1.length());
+ if (rename(glString1.toString(), glString2.toString()))
+ return false;
+ break;
+ case Delete:
+ if (delete(readString(readByte, 8).toString()))
+ return false;
+ break;
+ case Create:
+ if (create(readString(readByte, 8).toString(), readByte[8]))
+ return false;
+ break;
+ case ReadFile:
+ glString1 = readString(readByte, 8);
+ if (readFile(glString1.toString(),
+ arrToLongLE(readByte, 12+glString1.length()),
+ arrToLongLE(readByte, 12+glString1.length()+8)))
+ return false;
+ break;
+ case WriteFile:
+ if (writeFile(readString(readByte, 8).toString()))
+ return false;
+ break;
+ case SelectFile:
+ if (selectFile())
+ return false;
+ break;
+ case StartFile:
+ case EndFile:
+ if (startOrEndFile())
+ return false;
+ break;
+ case CMD_UNKNOWN:
+ default:
+ writeGL_FAIL(EXCEPTION_CAUGHT, "GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
+ }
+ }
+ }
+
+ protected GlString readString(byte[] readByte, int startPosition){
+ return new GlString010(readByte, startPosition);
+ }
+
+ protected boolean notGLCI(byte[] inputBytes){
+ return ! "GLCI".equals(new String(inputBytes, 0, 4, StandardCharsets.US_ASCII));
+ }
+
+ /**
+ * Handle StartFile & EndFile
+ * NOTE: It's something internal for GL and used somehow by GL-PC-app, so just ignore this, at least for v0.8.
+ * @return true - failed, false - passed
+ * */
+ protected boolean startOrEndFile(){
+ return writeGL_PASS("GL Handle 'StartFile' command");
+ }
+ /**
+ * Handle GetDriveCount
+ * 2 drives declared in current implementation
+ * @return true - failed, false - passed
+ */
+ protected boolean getDriveCount(){
+ return writeGL_PASS(intToArrLE(2),"GL Handle 'ListDrives' command");
+ }
+ /**
+ * Handle GetDriveInfo
+ * @return true - failed, false - passed
+ */
+ protected boolean getDriveInfo(int driveNo){
+ if (driveNo < 0 || driveNo > 1)
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetDriveInfo' command [no such drive]");
+
+ byte[] driveLabel,
+ driveLabelLen,
+ driveLetter,
+ driveLetterLen,
+ totalFreeSpace;
+ long totalSizeLong;
+
+ if (driveNo == 0){ // 0 == VIRTUAL DRIVE
+ driveLabel = "Virtual".getBytes(StandardCharsets.UTF_8);
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "VIRT".getBytes(StandardCharsets.UTF_8);
+ driveLetterLen = intToArrLE(driveLetter.length);
+ totalFreeSpace = new byte[4];
+ totalSizeLong = virtDriveSize;
+ }
+ else { //1 == User home dir
+ driveLabel = "Home".getBytes(StandardCharsets.UTF_8);
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "HOME".getBytes(StandardCharsets.UTF_8);
+ driveLetterLen = intToArrLE(driveLetter.length);
+ var userHomeDir = new File(System.getProperty("user.home"));
+ totalFreeSpace = Arrays.copyOfRange(longToArrLE(userHomeDir.getFreeSpace()), 0, 4);;
+ totalSizeLong = userHomeDir.getTotalSpace();
+ }
+ var totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4);
+
+ var command = Arrays.asList(
+ driveLabelLen,
+ driveLabel,
+ driveLetterLen,
+ driveLetter,
+ totalFreeSpace,
+ totalSize);
+
+ return writeGL_PASS(command, "GL Handle 'GetDriveInfo' command");
+ }
+ /**
+ * Handle SpecialPathCount
+ * Let's declare nothing. Write count of special paths
+ * @return true - failed, false - passed
+ * */
+ protected boolean getSpecialPathCount(){
+ return writeGL_PASS(intToArrLE(0), "GL Handle 'SpecialPathCount' command");
+ }
+ /**
+ * Handle SpecialPath
+ * @return true - failed, false - passed
+ * */
+ protected boolean getSpecialPath(int specialPathNo){
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'SpecialPath' command [not supported]");
+ }
+ /**
+ * Handle GetDirectoryCount & GetFileCount
+ * @return true - failed, false - passed
+ * */
+ protected boolean getDirectoryOrFileCount(String glFileName, boolean isGetDirectoryCount) {
+ if (glFileName.equals("VIRT:/")) {
+ return isGetDirectoryCount ?
+ writeGL_PASS("GL Handle 'GetDirectoryCount' command") :
+ writeGL_PASS(intToArrLE(nspMap.size()), "GL Handle 'GetFileCount' command Count = " + nspMap.size());
+ }
+ else if (glFileName.startsWith("HOME:/")){
+ var path = decodeGlPath(glFileName);
+ var pathDir = new File(path);
+
+ if (notExistsOrDirectory(pathDir))
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'GetDirectoryOrFileCount' command [doesn't exist or not a folder] "+ pathDir);
+
+ this.recentPath = path; // Save recent dir path
+ var filesOrDirs = isGetDirectoryCount ?
+ pathDir.list(this::isDirectoryAndNotHidden) :
+ pathDir.list(this::isFileAndNotHidden);
+
+ // If no folders, let's say 0;
+ if (filesOrDirs == null)
+ return writeGL_PASS("GL Handle 'GetDirectoryOrFileCount' command");
+ // Sorting is mandatory NOTE: Proxy tail
+ Arrays.sort(filesOrDirs, String.CASE_INSENSITIVE_ORDER);
+
+ if (isGetDirectoryCount)
+ this.recentDirs = filesOrDirs;
+ else
+ this.recentFiles = filesOrDirs;
+ // Otherwise, let's tell how may folders are in there
+ return writeGL_PASS(intToArrLE(filesOrDirs.length), "GL Handle 'GetDirectoryOrFileCount' command");
+ }
+ else if (glFileName.startsWith("SPEC:/")){
+ if (isGetDirectoryCount) // If dir request then 0 dirs
+ return writeGL_PASS("GL Handle 'GetDirectoryCount' command");
+ else if (selectedFile != null) // Else it's file request, if we have selected then we will report 1.
+ return writeGL_PASS(intToArrLE(1), "GL Handle 'GetFileCount' command Count = 1");
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+glFileName);
+ }
+ // If requested drive is not VIRT and not HOME then reply error
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+glFileName);
+ }
+
+ /**
+ * Handle GetDirectory
+ * @return true - failed, false - passed
+ * */
+ protected boolean getDirectory(String dirName, int subDirNo){
+ if (dirName.startsWith("HOME:/")) {
+ dirName = decodeGlPath(dirName);
+
+ var command = new ArrayList();
+
+ if (dirName.equals(recentPath) && recentDirs != null && recentDirs.length != 0){
+ var dirNameBytes = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(dirNameBytes.length));
+ command.add(dirNameBytes);
+ }
+ else {
+ var pathDir = new File(dirName);
+ // Make sure it's exists and it's path
+ if (notExistsOrDirectory(pathDir))
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
+ this.recentPath = dirName;
+ // Now collecting every folder or file inside
+ this.recentDirs = pathDir.list(this::isDirectoryAndNotHidden);
+ // Check that we still don't have any fuckups
+ if (this.recentDirs != null && this.recentDirs.length > subDirNo){
+ Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
+ var dirBytesName = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(dirBytesName.length));
+ command.add(dirBytesName);
+ }
+ else
+ return writeGL_FAIL(INVALID_INDEX,"GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
+ }
+ return writeGL_PASS(command, "GL Handle 'GetDirectory' command.");
+ }
+ // VIRT:// and any other
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetDirectory' command for virtual drive [no folders support]");
+ }
+ /**
+ * Handle GetFile
+ * @return true - failed, false - passed
+ * */
+ protected boolean getFile(String glDirName, int subDirNo){
+ var command = new LinkedList();
+
+ if (glDirName.startsWith("HOME:/")) {
+ var dirName = decodeGlPath(glDirName);
+
+ if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
+ var fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ }
+ else {
+ var pathDir = new File(dirName);
+ if (notExistsOrDirectory(pathDir))
+ writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetFile' command [doesn't exist or not a folder]");
+ this.recentPath = dirName;
+ this.recentFiles = pathDir.list(this::isFileAndNotHidden);
+ // Check that we still don't have any fuckups
+ if (this.recentFiles != null && this.recentFiles.length > subDirNo){
+ Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
+ var fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ }
+ else
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetFile' command [doesn't exist or not a folder]");
+ }
+ return writeGL_PASS(command, "GL Handle 'GetFile' command.");
+ }
+ else if (glDirName.equals("VIRT:/") && (! nspMap.isEmpty())){ // thus nspMapKeySetIndexes also != 0
+ var fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ return writeGL_PASS(command, "GL Handle 'GetFile' command.");
+ }
+ else if (glDirName.equals("SPEC:/") && (selectedFile != null)){
+ var fileNameBytes = selectedFile.getName().getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ return writeGL_PASS(command, "GL Handle 'GetFile' command.");
+ }
+ // any other cases
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetFile' command for virtual drive [no folders support?]");
+ }
+ /**
+ * Handle StatPath
+ * @return true - failed, false - passed
+ * */
+ protected boolean statPath(String glFileName){
+ var command = new ArrayList();
+
+ if (glFileName.startsWith("HOME:/")){
+ var fileDirElement = new File(decodeGlPath(glFileName));
+
+ if (fileDirElement.exists()){
+ if (fileDirElement.isDirectory())
+ command.add(GL_OBJECT_TYPE_DIR);
+ else {
+ command.add(GL_OBJECT_TYPE_FILE);
+ command.add(longToArrLE(fileDirElement.length()));
+ }
+ return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName);
+ }
+ }
+ else if (glFileName.startsWith("VIRT:/")) {
+ var fileName = glFileName.replaceFirst("^.*?:/", "");
+ if (nspMap.containsKey(fileName)){
+ command.add(GL_OBJECT_TYPE_FILE); // THIS IS INT
+ if (nspMap.get(fileName).isDirectory())
+ command.add(longToArrLE(splitFileSize.get(fileName))); // YES, THIS IS LONG!;
+ else
+ command.add(longToArrLE(nspMap.get(fileName).length())); // YES, THIS IS LONG!
+
+ return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName);
+ }
+ }
+ else if (glFileName.startsWith("SPEC:/")){
+ var fileName = glFileName.replaceFirst("^.*?:/", "");
+ if (selectedFile.getName().equals(fileName)){
+ command.add(GL_OBJECT_TYPE_FILE);
+ command.add(longToArrLE(selectedFile.length()));
+ return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName);
+ }
+ }
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'StatPath' command [no such path]: "+glFileName);
+ }
+ /**
+ * Handle 'Rename' that is actually 'mv'
+ * @return true - failed, false - passed
+ * */
+ protected boolean rename(String glFileName, String glNewFileName){
+ if (glFileName.startsWith("HOME:/")){
+ // Prevent GL failures
+ this.recentPath = null;
+ this.recentFiles = null;
+ this.recentDirs = null;
+
+ var fileName = decodeGlPath(glFileName);
+ var newFile = new File(decodeGlPath(glNewFileName));
+
+ try {
+ if (new File(fileName).renameTo(newFile)){
+ return writeGL_PASS("GL Handle 'Rename' command.");
+ }
+ }
+ catch (SecurityException se){
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Rename' command failed:\n\t" +se.getMessage());
+ }
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Rename' command is not supported for virtual drive, selected files," +
+ " if file with such name already exists in folder, read-only directories");
+ }
+ /**
+ * Handle 'Delete'
+ * @return true - failed, false - passed
+ * */
+ protected boolean delete(String glFileName) {
+ if (! glFileName.startsWith("HOME:/"))
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory] "+glFileName);
+
+ var file = new File(decodeGlPath(glFileName));
+ try {
+ if (file.delete())
+ return writeGL_PASS("GL Handle 'Rename' command.");
+ }
+ catch (SecurityException ignored){} // Ah, leave it
+
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Create' command [unknown drive/read-only directory]");
+ }
+ /**
+ * Handle 'Create'
+ * @param type 1 â file, 2 â folder
+ * @param glFileName full path including new file name in the end
+ * @return true - failed, false - passed
+ * */
+ protected boolean create(String glFileName, byte type) {
+ if (! glFileName.startsWith("HOME:/")) // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Create' command [not supported for virtual drive/wrong drive/read-only directory]"+glFileName);
+
+ var file = new File(decodeGlPath(glFileName));
+ try {
+ boolean result = switch (type) {
+ case 1 -> file.createNewFile();
+ case 2 -> file.mkdir();
+ default -> false;
+ };
+
+ if (result)
+ return writeGL_PASS("GL Handle 'Create' command.");
+ }
+ catch (SecurityException | IOException ignored){}
+
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'Create' command [unknown drive/read-only directory]");
+ }
+
+ /**
+ * Handle 'ReadFile'
+ * @param glFileName full path including new file name in the end in format of Goldleaf
+ * @param offset requested offset
+ * @param size requested size
+ * @return true - failed, false - passed
+ * */
+ protected boolean readFile(String glFileName, long offset, long size) {
+ var fileName = glFileName.replaceFirst("^.*?:/", "");
+ if (glFileName.startsWith("VIRT:/")){ // Could have split-file
+ // Let's find out which file requested
+ var fNamePath = nspMap.get(fileName).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
+ // If we don't have this file opened, let's open it
+ if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fNamePath))) {
+ if (openReadFileNameAndPath != null) // (Try to) close what opened
+ closeRAFandSplitReader();
+ try{ // And open the rest
+ var tempFile = nspMap.get(fileName);
+ if (tempFile.isDirectory()) {
+ randAccessFile = null;
+ splitReader = new NSSplitReader(tempFile, 0);
+ }
+ else {
+ splitReader = null;
+ randAccessFile = new RandomAccessFile(tempFile, "r");
+ }
+ openReadFileNameAndPath = fNamePath;
+ }
+ catch (IOException | NullPointerException ioe){
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ }
+ else { // SPEC:/ & HOME:/
+ String filePath;
+
+ if (glFileName.startsWith("SPEC:/")) {
+ if (! fileName.equals(selectedFile.getName())) {
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' command\n\trequested != selected:\n\t"
+ + glFileName + "\n\t" + selectedFile);
+ }
+ filePath = selectedFile.getAbsolutePath();
+ }
+ else {
+ filePath = decodeGlPath(glFileName); // What requested?
+ }
+ // If we don't have this file opened, let's open it
+ if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(filePath))) {
+ if (openReadFileNameAndPath != null) // Try close what opened
+ closeRAF();
+ try{ // Open what has to be opened
+ randAccessFile = new RandomAccessFile(filePath, "r");
+ openReadFileNameAndPath = filePath;
+ }catch (IOException | NullPointerException ioe){
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ }
+ //----------------------- Actual transfer chain ------------------------
+ try{
+ var chunk = new byte[(int)size];
+ int bytesRead;
+
+ if (randAccessFile == null){
+ splitReader.seek(offset);
+ bytesRead = splitReader.read(chunk); // How many bytes we got?
+ }
+ else {
+ randAccessFile.seek(offset);
+ bytesRead = randAccessFile.read(chunk); // How many bytes we got?
+ }
+
+ if (bytesRead != (int) size) // Let's check that we read expected size
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' command [CMD]" +
+ "\n At offset: " + offset +
+ "\n Requested: " + size +
+ "\n Received: " + bytesRead);
+ if (writeGL_PASS(longToArrLE(size), "GL Handle 'ReadFile' command [CMD]")) { // Reporting result
+ return true;
+ }
+ if (writeToUsb(chunk)) { // Bypassing bytes we read total // FIXME: move failure message into method
+ print("GL Handle 'ReadFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ catch (Exception ioe){
+ closeOpenedReadFilesGl();
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' transfer chain\n\t"+ioe.getMessage());
+ }
+ }
+ /**
+ * Handle 'WriteFile'
+ * @param glFileName full path including new file name in the end
+ * @return true - failed, false - passed
+ * */
+ boolean writeFile(String glFileName) {
+ if (glFileName.startsWith("VIRT:/"))
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'WriteFile' command [not supported for virtual drive]");
+
+ glFileName = decodeGlPath(glFileName);
+ // Check if this file being used during this session
+ if (! writeFilesMap.containsKey(glFileName)){
+ try{ // If this file exists GL will take care; Otherwise, let's add it
+ writeFilesMap.put(glFileName,
+ new BufferedOutputStream(new FileOutputStream(glFileName, true))); // Open what we have to open
+ } catch (IOException ioe){
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'WriteFile' command [IOException]\n\t"+ioe.getMessage());
+ }
+ }
+
+ var transferredData = readGL_file();
+
+ if (transferredData == null){
+ print("GL Handle 'WriteFile' command [1/1]", EMsgType.FAIL);
+ return true;
+ }
+ try{
+ writeFilesMap.get(glFileName).write(transferredData, 0, transferredData.length);
+ }
+ catch (IOException ioe){
+ return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'WriteFile' command [1/1]\n\t"+ioe.getMessage());
+ }
+ // Report we're good
+ return writeGL_PASS("GL Handle 'WriteFile' command");
+ }
+
+ /**
+ * Handle 'SelectFile'
+ * @return true - failed, false - passed
+ * */
+ protected boolean selectFile(){
+ var selectedFile = CompletableFuture.supplyAsync(() -> {
+ var fChooser = new FileChooser();
+ fChooser.setTitle(MediatorControl.INSTANCE.getResourceBundle().getString("btn_OpenFile")); // TODO: FIX BAD IMPLEMENTATION
+ fChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: Consider fixing; not a priority.
+ fChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("*", "*"));
+ return fChooser.showOpenDialog(null); // Leave as is for now.
+ }, Platform::runLater).join();
+
+ if (selectedFile == null){ // Nothing selected
+ this.selectedFile = null;
+ return writeGL_FAIL(SELECTION_CANCELLED, "GL Handle 'SelectFile' command: Nothing selected");
+ }
+
+ var selectedFileNameBytes = ("SPEC:/"+selectedFile.getName()).getBytes(StandardCharsets.UTF_8);
+ var command = Arrays.asList(
+ intToArrLE(selectedFileNameBytes.length),
+ selectedFileNameBytes);
+ if (writeGL_PASS(command, "GL Handle 'SelectFile' command")) {
+ this.selectedFile = null;
+ return true;
+ }
+ this.selectedFile = selectedFile;
+ return false;
+ }
+
+ /*--------------------------------*/
+ /* GL HELPERS */
+ /*--------------------------------*/
+ /**
+ * Convert path received from GL to host-default structure
+ */
+ protected String decodeGlPath(String glPath){
+ if (isWindows)
+ glPath = glPath.replace('/', '\\');
+ return homePath + glPath.substring(5); // e.g. HOME:/some/file/
+ }
+
+ private boolean isFileAndNotHidden(File parent, String child){
+ var entry = new File(parent, child);
+ return (! entry.isDirectory()) && (nspFilter ?
+ child.toLowerCase().endsWith(".nsp") :
+ ! entry.isHidden());
+ }
+
+ private boolean isDirectoryAndNotHidden(File parent, String child){
+ var dir = new File(parent, child);
+ return (dir.isDirectory() && ! dir.isHidden());
+ }
+
+ private boolean notExistsOrDirectory(File pathDir){
+ return (! pathDir.exists() ) || (! pathDir.isDirectory());
+ }
+
+ /**
+ * Close files opened for read/write
+ */
+ protected void closeOpenedReadFilesGl(){
+ if (openReadFileNameAndPath != null){
+ closeRAFandSplitReader();
+ openReadFileNameAndPath = null;
+ randAccessFile = null;
+ splitReader = null;
+ }
+ }
+ protected void closeRAFandSplitReader(){
+ closeRAF();
+ try{
+ splitReader.close();
+ }
+ catch (IOException ioe_){
+ print("Unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
+ }
+ catch (Exception ignored){}
+ }
+ protected void closeRAF(){
+ try{
+ randAccessFile.close();
+ }
+ catch (IOException ioe_){
+ print("Unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
+ }
+ catch (Exception ignored){}
+ }
+ /*----------------------------------------------------*/
+ /* GL READ/WRITE USB SPECIFIC */
+ /*----------------------------------------------------*/
+
+ protected byte[] readGL(){
+ var readBuffer = ByteBuffer.allocateDirect(PACKET_SIZE);
+ var readBufTransferred = IntBuffer.allocate(1);
+
+ while (! task.isCancelled()) {
+ int result = LibUsb.bulkTransfer(handlerNS, IN_EP, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ var receivedBytes = new byte[readBufTransferred.get()];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ closeOpenedReadFilesGl(); // Could be a problem if GL glitches and slow down process. Or if user has extra-slow SD card. TODO: refactor?
+ continue;
+ default:
+ print("GL Data transfer issue [read]\n Returned: " +
+ LibUsb.errorName(result) +
+ "\n GL Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ print("GL Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+ private byte[] readGL_file(){
+ var readBuffer = ByteBuffer.allocateDirect(8388608); // Just don't ask..
+ var readBufTransferred = IntBuffer.allocate(1);
+
+ while (! task.isCancelled() ) {
+ int result = LibUsb.bulkTransfer(handlerNS, IN_EP, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ var receivedBytes = new byte[readBufTransferred.get()];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ print("GL Data transfer issue [read]\n Returned: " +
+ LibUsb.errorName(result) +
+ "\n GL Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ print("GL Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+ /**
+ * Write new command
+ * */
+ protected boolean writeGL_PASS(String onFailureText){
+ if (writeToUsb(CMD_GLCO_SUCCESS)){
+ print(onFailureText, EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ protected boolean writeGL_PASS(byte[] message, String onFailureText){
+ var result = writeToUsb(ByteBuffer.allocate(PACKET_SIZE)
+ .put(CMD_GLCO_SUCCESS_FLAG)
+ .put(message)
+ .array());
+
+ if(result){
+ print(onFailureText, EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ protected boolean writeGL_PASS(List messages, String onFailureText){
+ var writeBuffer = ByteBuffer.allocate(PACKET_SIZE)
+ .put(CMD_GLCO_SUCCESS_FLAG);
+ messages.forEach(writeBuffer::put);
+ if (writeToUsb(writeBuffer.array())){
+ print(onFailureText, EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+
+ protected boolean writeGL_FAIL(byte[] failurePacket, String failureMessage){
+ if (writeToUsb(failurePacket)){
+ print(failureMessage, EMsgType.WARNING);
+ return true;
+ }
+ print(failureMessage, EMsgType.FAIL);
+ return false;
+ }
+ /**
+ * Sending any byte array to USB device
+ * @return 'false' if no issues
+ * 'true' if errors happened
+ * */
+ private boolean writeToUsb(byte[] message){
+ //RainbowHexDump.hexDumpUTF16LE(message); // DEBUG
+ var writeBufTransferred = IntBuffer.allocate(1);
+
+ while (! task.isCancelled()) {
+ int result = LibUsb.bulkTransfer(handlerNS,
+ OUT_EP,
+ ByteBuffer.allocateDirect(message.length).put(message), // order -> BIG_ENDIAN; Don't writeBuffer.rewind();
+ writeBufTransferred,
+ 1000); // TIMEOUT. 0 stands for infinite. Endpoint OUT = 0x01
+
+ switch (result){
+ case LibUsb.SUCCESS:
+ if (writeBufTransferred.get() == message.length)
+ return false;
+ else {
+ print("GL Data transfer issue [write]\n Requested: " +
+ message.length +
+ "\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
+ return true;
+ }
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ print("GL Data transfer issue [write]\n Returned: " +
+ LibUsb.errorName(result) +
+ "\n GL Execution stopped", EMsgType.FAIL);
+ return true;
+ }
+ }
+ print("GL Execution interrupted", EMsgType.INFO);
+ return true;
+ }
+}
diff --git a/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_111.java b/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_111.java
new file mode 100644
index 0000000..7ed498c
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/GoldLeaf_111.java
@@ -0,0 +1,118 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+*/
+package nsusbloader.com.usb.gl;
+
+import nsusbloader.ModelControllers.CancellableRunnable;
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import org.usb4java.DeviceHandle;
+
+import java.io.*;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+
+import static nsusbloader.com.usb.gl.Converters.*;
+
+/**
+ * GoldLeaf 1.1.1 processing
+ */
+public class GoldLeaf_111 extends GoldLeaf_010{
+
+ public GoldLeaf_111(DeviceHandle handler, LinkedHashMap nspMap,
+ CancellableRunnable task,
+ ILogPrinter logPrinter,
+ boolean nspFilter) {
+ super(handler, nspMap, task, logPrinter, nspFilter);
+ }
+
+ @Override
+ protected void printWelcomeMessage(){
+ print("=========== GoldLeaf v1.1.1 ===========\n\t" +
+ "VIRT:/ equals files added into the application\n\t" +
+ "HOME:/ equals " + homePath + "\n\t" +
+ "BE CAREFUL!\n\t" +
+ "Due to some strange behaviour with Goldleaf v1.1.1, you will see last menu entry " +
+ "'Do not click (crashes Atmosphere)'\n\t" +
+ "You should better not clicking on it", EMsgType.INFO);
+ }
+
+ /**
+ * Fixes issues with incorrect request for 'Home' & 'Virtual'. Forces both to return 'HOME:/'
+ * v1.1.1 specific fix
+ * Otherwise v.1.1.1 returns 'HOME:/' once 'Virtual' requested and ':/' once requested 'Home'
+ * */
+
+ @Override
+ protected boolean getDriveCount(){
+ return writeGL_PASS(intToArrLE(3),"GL Handle 'ListDrives' command");
+ }
+
+ @Override
+ protected boolean getDriveInfo(int driveNo){
+ if (driveNo < 0 || driveNo > 2)
+ return writeGL_FAIL(INVALID_INDEX, "GL Handle 'GetDriveInfo' command [no such drive]");
+
+ byte[] driveLabel,
+ driveLabelLen,
+ driveLetter,
+ driveLetterLen,
+ totalFreeSpace;
+ long totalSizeLong;
+
+ switch (driveNo){
+ case 0:
+ driveLabel = "Home".getBytes(StandardCharsets.UTF_8); // yes, it's hotfix
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "VIRT".getBytes(StandardCharsets.UTF_8); // and this is fine
+ driveLetterLen = intToArrLE(driveLetter.length);
+ totalFreeSpace = new byte[4];
+ totalSizeLong = virtDriveSize;
+ break;
+ case 1:
+ driveLabel = "Virtual".getBytes(StandardCharsets.UTF_8); // here as well
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "HOME".getBytes(StandardCharsets.UTF_8); // and here
+ driveLetterLen = intToArrLE(driveLetter.length);
+ var userHomeDir = new File(System.getProperty("user.home"));
+ totalFreeSpace = Arrays.copyOfRange(longToArrLE(userHomeDir.getFreeSpace()), 0, 4);;
+ totalSizeLong = userHomeDir.getTotalSpace();
+ break;
+ default:
+ driveLabel = "Do not click (crashes Atmosphere)".getBytes(StandardCharsets.UTF_8); // and this one is necessary too
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "VIRT".getBytes(StandardCharsets.UTF_8);
+ driveLetterLen = intToArrLE(driveLetter.length);
+ totalFreeSpace = new byte[4];
+ totalSizeLong = virtDriveSize;
+ break;
+ }
+
+ var totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4);
+
+ var command = Arrays.asList(
+ driveLabelLen,
+ driveLabel,
+ driveLetterLen,
+ driveLetter,
+ totalFreeSpace,
+ totalSize);
+
+ return writeGL_PASS(command, "GL Handle 'GetDriveInfo' command");
+ }
+}
diff --git a/src/main/java/nsusbloader/com/usb/gl/GoldleafCmd.java b/src/main/java/nsusbloader/com/usb/gl/GoldleafCmd.java
new file mode 100644
index 0000000..be6cf76
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/gl/GoldleafCmd.java
@@ -0,0 +1,54 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+ */
+package nsusbloader.com.usb.gl;
+
+public enum GoldleafCmd {
+ GetDriveCount((byte) 1),
+ GetDriveInfo((byte) 2),
+ StatPath((byte) 3),
+ GetFileCount((byte) 4),
+ GetFile((byte) 5),
+ GetDirectoryCount((byte) 6),
+ GetDirectory((byte) 7),
+ StartFile((byte) 8),
+ ReadFile((byte) 9),
+ WriteFile((byte) 10),
+ EndFile((byte) 11),
+ Create((byte) 12),
+ Delete((byte) 13),
+ Rename((byte) 14),
+ GetSpecialPathCount((byte) 15),
+ GetSpecialPath((byte) 16),
+ SelectFile((byte) 17),
+ CMD_UNKNOWN((byte) 255);
+
+ private final byte id;
+
+ GoldleafCmd(byte id) {
+ this.id = id;
+ }
+
+ public static GoldleafCmd get(byte id) {
+ for(GoldleafCmd cmd : values()) {
+ if(cmd.id == id)
+ return cmd;
+ }
+ return CMD_UNKNOWN;
+ }
+}
\ No newline at end of file
diff --git a/src/main/resources/SettingsBlockGeneric.fxml b/src/main/resources/SettingsBlockGeneric.fxml
index 2db10eb..a62ebbc 100644
--- a/src/main/resources/SettingsBlockGeneric.fxml
+++ b/src/main/resources/SettingsBlockGeneric.fxml
@@ -15,8 +15,8 @@
-
-
+
+
diff --git a/src/main/resources/locale_sr_RS_#LATN.properties b/src/main/resources/locale_sr_RS_#LATN.properties
new file mode 100644
index 0000000..6df3ac0
--- /dev/null
+++ b/src/main/resources/locale_sr_RS_#LATN.properties
@@ -0,0 +1,97 @@
+btn_OpenFile=Izaberite datoteke
+btn_OpenFolders=Izaberite fasciklu
+btn_Upload=Otpremi na NS
+btn_OpenFolders_tooltip=Izaberite fasciklu koju \u017Eelite da se skenira.\nIzabrana fascikla i sve podfascikle \u0107e biti skenirane.\nSve odgovaraju\u0107e datoteke \u0107e biti dodane na listu.
+tab3_Txt_EnteredAsMsg1=Upisani ste kao:
+tab3_Txt_EnteredAsMsg2=Potrebno je imati root privilegije ili podesiti "udev" pravila za ovog korisnika kako bi se izbegli bilo kakvi problemi.
+tab3_Txt_FilesToUploadTitle=Datoteke za otpremanje:
+tab3_Txt_GreetingsMessage=Dobrodo\u0161li u NS-USBloader
+tab3_Txt_NoFolderOrFileSelected=Nije izabrana nijedna datoteka: Nema ni\u0161ta za otpremanje.
+windowBodyConfirmExit=Prenos podataka je u toku i zatvaranje ove aplikacije \u0107e je prekinuti.\nTo je najgore \u0161to mo\u017Eete u\u010Diniti u ovom trenutku.\Da li \u017Eelite da prekinete proces i iza\u0111ete?
+windowTitleConfirmExit=Ne, nemoj to uraditi!
+btn_Stop=Prekini
+tab3_Txt_GreetingsMessage2=--\n\
+Source: https://git.redrise.ru/desu/ns-usbloader\n\
+Mirror: https://github.com/developersu/ns-usbloader/\n\
+Site: https://redrise.ru\n\
+Dmitry Isaenko [developer.su]
+tab1_table_Lbl_Status=Status
+tab1_table_Lbl_FileName=Naziv datoteke
+tab1_table_Lbl_Size=Veli\u010Dina
+tab1_table_Lbl_Upload=Otpremi?
+tab1_table_contextMenu_Btn_BtnDelete=Ukloni
+tab1_table_contextMenu_Btn_DeleteAll=Ukloni sve
+tab2_Lbl_HostIP=Host IP
+tab1_Lbl_NSIP=NS IP:
+tab2_Cb_ValidateNSHostName=Uvijek proverite uneseni NS IP.
+windowBodyBadIp=Da li ste sigurni da ste ispravno uneli NS IP adresu?
+windowTitleBadIp=NS IP adresa je verovatno neta\u010Dna.
+tab2_Cb_ExpertMode=Napredni re\u017Eim (NET pode\u0161avanje)
+tab2_Lbl_HostPort=port
+tab2_Cb_AutoDetectIp=Automatski prona\u0111i IP
+tab2_Cb_RandSelectPort=Nasumi\u010Dno dodeli port
+tab2_Cb_DontServeRequests=Ne uslu\u017Euj zahteve
+tab2_Lbl_DontServeRequestsDesc=Ako je opcija izabrana, ovaj ra\u010Dunar nec\u0301e odgovarati na zahteve NSP datoteka koje dolaze od NS-a (preko mre\u017Ee) i koristiti definisana pode\u0161avanja host-a da ka\u017Ee Awoo Installer-u (ili kompatibilnim aplikacijama) gde treba da tra\u017Ei datoteke.
+tab2_Lbl_HostExtra=dodatno
+windowTitleErrorPort=Port je pogre\u0161no pode\u0161en!
+windowBodyErrorPort=Port ne mo\u017Ee biti 0 ili vec\u0301i od 65535.
+tab2_Cb_AutoCheckForUpdates=Automatski proveri a\u017Euriranja
+windowTitleNewVersionAval=Nova verzija dostupna
+windowTitleNewVersionNOTAval=Nema dostupnih novih verzija
+windowTitleNewVersionUnknown=Nije moguc\u0301e proveriti da li postoje nove verzije
+windowBodyNewVersionUnknown=Ne\u0161to nije u redu\nMo\u017Eda Internet konekcija nije dostupna ili GitHub ne radi
+windowBodyNewVersionNOTAval=Koristite najnoviju verziju
+tab2_Cb_AllowXciNszXcz=Dozvolite izbor XCI / NSZ / XCZ datoteka za Awoo
+tab2_Lbl_AllowXciNszXczDesc=Kori\u0161teno od strane aplikacija koje podr\u017Eavaju XCI/NSZ/XCZ i koriste Awoo (poznatiji kao Adubbz/TinFoil) protokol za prenos. Ne menjajte ako niste sigurni. Omoguc\u0301i za Awoo Installer.
+tab2_Lbl_Language=Jezik
+windowBodyRestartToApplyLang=Ponovo pokrenite aplikaciju da biste primenili promene.
+btn_OpenSplitFile=Izaberite razdvojene
+tab2_Lbl_ApplicationSettings=Glavna pode\u0161avanja
+tabSplMrg_Lbl_SplitNMergeTitle=Alat za razdvajanje i spajanje datoteka
+tabSplMrg_RadioBtn_Split=Razdvoji
+tabSplMrg_RadioBtn_Merge=Spoji
+tabSplMrg_Txt_File=Datoteka:
+tabSplMrg_Txt_Folder=Razdvoji datoteku (fasciklu):
+tabSplMrg_Btn_SelectFile=Izaberite datoteku
+tabSplMrg_Btn_SelectFolder=Izaberite fasciklu
+tabSplMrg_Lbl_SaveToLocation=Sa\u010Duvaj na:
+tabSplMrg_Btn_ChangeSaveToLocation=Promeni
+tabSplMrg_Btn_Convert=Pretvori
+windowTitleError=Gre\u0161ka
+windowBodyPleaseFinishTransfersFirst=Nije moguc\u0301e razdvojiti/spojiti datoteke kada je aktivan USB/mre\u017Eni proces aplikacije. Najpre prekinite aktivne transfere.
+done_txt=Gotovo!
+failure_txt=Nije uspelo
+btn_Select=Izaberi
+btn_InjectPayloader=Po\u0161alji payloadbtn_OpenSplitFile
+tabNXDT_Btn_Start=Po\u010Dni!
+tab2_Btn_InstallDrivers=Preuzmi i instaliraj drajvere
+windowTitleDownloadDrivers=Preuzmi i instaliraj drajvere
+windowBodyDownloadDrivers=Preuzimanje drajvera (libusbK v3.0.7.0)...
+btn_Cancel=Otka\u017Ei
+btn_Close=Zatvori
+tab2_Cb_GlVersion=GoldLeaf verzija
+tab2_Cb_GLshowNspOnly=Prika\u017Ei samo *.nsp u GoldLeaf-u.
+windowBodyPleaseStopOtherProcessFirst=Zaustavite druge aktivne procese pre nego \u0161to nastavite.
+tab2_Cb_foldersSelectorForRoms=Izaberite fasciklu sa ROM datotekama umesto da birate ROM datoteke pojedina\u010Dno.
+tab2_Cb_foldersSelectorForRomsDesc=Menja pona\u0161anje dugmeta "Izaberi datoteke" na kartici "Igre": umesto da birate ROM datoteke jednu po jednu, mo\u017Eete izabrati fasciklu da biste dodali sve podr\u017Eane datoteke odjednom.
+windowTitleAddingFiles=Tra\u017Eenje datoteka...
+windowBodyFilesScanned=Skenirano datoteka: %d\nBi\u0107e dodano: %d
+tab2_Lbl_AwooBlockTitle=Awoo Installer i kompatibilni
+tabRcm_Lbl_Payload=Payload:
+tabRcm_Lbl_FuseeGelee=Fus\u00E9e Gel\u00E9e RCM
+tabPatches_Lbl_Firmware=Firmware:
+tabPatches_Lbl_Atmo=Atmosphere:
+tabPatches_Btn_fromFolder=Iz fascikle
+tabPatches_Btn_asZipFile=kao ZIP datoteka
+tabPatches_Lbl_Title=Zakrpe
+tabPatches_Lbl_Keys=Klju\u010Devi:
+tabPatches_Btn_MakeEs=Napravi ES
+tabPatches_Btn_MakeFs=Napravi FS
+tabPatches_Btn_MakeAtmo=Napravi Loader (Atmosphere)
+tabPatches_Btn_MakeAll=Napravi sve
+tabPatches_ServiceWindowMessageEsFs=I firmware i klju\u010Devi treba da budu pode\u0161eni da bi se generisale zakrpe. U suprotnom, nije jasno \u0161ta treba zakrpiti.
+tabPatches_ServiceWindowMessageLoader=Fascikla Atmosphere treba da bude definisana da bi se generisala "Loader" zakrpa.
+tab2_Btn_ApplicationFont=Promenite font aplikacije
+btn_ResetToDefaults=Resetuj
+fontPreviewText=Pregled teksta
+fontSize=Veli\u010Dina fonta:
\ No newline at end of file
diff --git a/src/test/java/integration/EsIntegrationTest.java b/src/test/java/integration/EsIntegrationTest.java
index 8fb868c..29b0713 100644
--- a/src/test/java/integration/EsIntegrationTest.java
+++ b/src/test/java/integration/EsIntegrationTest.java
@@ -22,18 +22,17 @@ public class EsIntegrationTest {
pathToKeysFile = environment.getProdkeysLocation();
saveTo = environment.getSaveToLocation() + File.separator + "ES_LPR";
pathToFirmwares = environment.getFirmwaresLocation();
- pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
+ pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 19.0.1";
}
@DisplayName("ES Integration validation - everything")
@Test
void makeEss() throws Exception{
- File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
- return s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*");
- //return s.matches("^Firmware 10.0.1.*");
- });
+ File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
+ s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*"));
assert fwDirs != null;
Arrays.sort(fwDirs);
+ Arrays.stream(fwDirs).forEach(System.out::println);
for (File dir : fwDirs){
EsPatchMaker esPatchMaker = new EsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
Thread workThread = new Thread(esPatchMaker);
@@ -42,6 +41,7 @@ public class EsIntegrationTest {
}
}
+ @Disabled
@DisplayName("ES Integration validation - one particular firmware")
@Test
void makeEs() throws Exception{
diff --git a/src/test/java/integration/FsIntegrationTest.java b/src/test/java/integration/FsIntegrationTest.java
index f56544c..b2da6b3 100644
--- a/src/test/java/integration/FsIntegrationTest.java
+++ b/src/test/java/integration/FsIntegrationTest.java
@@ -25,19 +25,17 @@ public class FsIntegrationTest {
pathToKeysFile = environment.getProdkeysLocation();
saveTo = environment.getSaveToLocation() + File.separator + "FS_LPR";
pathToFirmwares = environment.getFirmwaresLocation();
- pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
+ pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 15.0.0";
}
@DisplayName("FS Integration validation - everything")
@Test
void makeFss() throws Exception{
- File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
- return (s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
- //return s.matches("^Firmware 10.0.1.*");
- });
+ File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
+ s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
assert fwDirs != null;
Arrays.sort(fwDirs);
-
+ Arrays.stream(fwDirs).forEach(System.out::println);
for (File dir : fwDirs){
System.out.println("\n\t\t\t"+dir.getName());
FsPatchMaker fsPatchMaker = new FsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
@@ -47,6 +45,7 @@ public class FsIntegrationTest {
}
}
+ @Disabled
@DisplayName("FS Integration validation - one particular firmware")
@Test
void makeFs() throws Exception{