Compare commits
6 commits
master
...
experiment
Author | SHA1 | Date | |
---|---|---|---|
![]() |
c8efebfcff | ||
![]() |
0a9883ad2b | ||
![]() |
02a2200d04 | ||
![]() |
cf7d54d8af | ||
![]() |
0ce56eb9b4 | ||
![]() |
37db634a23 |
17 changed files with 1276 additions and 88 deletions
|
@ -1,7 +1,7 @@
|
||||||
steps:
|
steps:
|
||||||
- name: test-standard
|
- name: test-standard
|
||||||
when:
|
when:
|
||||||
event: [tag, push]
|
event: [tag, push, manual]
|
||||||
image: maven:3-openjdk-17
|
image: maven:3-openjdk-17
|
||||||
commands:
|
commands:
|
||||||
- mvn -B -DskipTests clean package
|
- mvn -B -DskipTests clean package
|
||||||
|
@ -14,7 +14,7 @@ steps:
|
||||||
|
|
||||||
- name: make-windows-installer
|
- name: make-windows-installer
|
||||||
when:
|
when:
|
||||||
event: [tag, push]
|
event: [tag, push, manual]
|
||||||
image: wheatstalk/makensis:3
|
image: wheatstalk/makensis:3
|
||||||
commands:
|
commands:
|
||||||
- cp target/NS-USBloader.exe misc/windows/NSIS/
|
- cp target/NS-USBloader.exe misc/windows/NSIS/
|
||||||
|
@ -32,7 +32,7 @@ steps:
|
||||||
|
|
||||||
- name: emerge-legacy-artifact
|
- name: emerge-legacy-artifact
|
||||||
when:
|
when:
|
||||||
event: [tag, push]
|
event: [tag, push, manual]
|
||||||
image: maven:3-openjdk-17
|
image: maven:3-openjdk-17
|
||||||
commands:
|
commands:
|
||||||
- . ./.make_legacy
|
- . ./.make_legacy
|
||||||
|
@ -44,7 +44,7 @@ steps:
|
||||||
|
|
||||||
- name: make-legacy-windows-installer
|
- name: make-legacy-windows-installer
|
||||||
when:
|
when:
|
||||||
event: [tag, push]
|
event: [tag, push, manual]
|
||||||
image: wheatstalk/makensis:3
|
image: wheatstalk/makensis:3
|
||||||
commands:
|
commands:
|
||||||
- cp target/NS-USBloader.exe misc/windows/NSIS/
|
- cp target/NS-USBloader.exe misc/windows/NSIS/
|
||||||
|
@ -60,7 +60,7 @@ steps:
|
||||||
|
|
||||||
- name: emerge-mac-m1-artifact
|
- name: emerge-mac-m1-artifact
|
||||||
when:
|
when:
|
||||||
event: [tag, push]
|
event: [tag, push, manual]
|
||||||
image: maven:3-openjdk-17
|
image: maven:3-openjdk-17
|
||||||
commands:
|
commands:
|
||||||
- . ./.make_m1
|
- . ./.make_m1
|
||||||
|
|
|
@ -67,7 +67,7 @@ Sometimes I add new posts about this project [on my blog page](https://developer
|
||||||
|
|
||||||
### System requirements
|
### 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`)
|
- libusb, if you have a Mac with Apple Silicon (install via `brew install libusb`)
|
||||||
|
|
||||||
### Supported Goldleaf versions
|
### Supported Goldleaf versions
|
||||||
|
@ -78,7 +78,8 @@ Sometimes I add new posts about this project [on my blog page](https://developer
|
||||||
| v0.6.1 | v0.6 |
|
| v0.6.1 | v0.6 |
|
||||||
| v0.7 - 0.7.3 | v0.7+ |
|
| v0.7 - 0.7.3 | v0.7+ |
|
||||||
| v0.8 - 0.9 | v1.0+ |
|
| v0.8 - 0.9 | v1.0+ |
|
||||||
| v0.10 | v6.0+ |
|
| v0.10 - 1.0.0 | v6.0+ |
|
||||||
|
| v1.0.0 | v7.3+ |
|
||||||
|
|
||||||
where '+' means 'any next NS-USBloader version'.
|
where '+' means 'any next NS-USBloader version'.
|
||||||
|
|
||||||
|
|
6
pom.xml
6
pom.xml
|
@ -50,7 +50,7 @@
|
||||||
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
|
||||||
<maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
|
<maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
|
||||||
<javafx.version>19.0.2.1</javafx.version>
|
<javafx.version>19.0.2.1</javafx.version>
|
||||||
<maven.compiler.release>11</maven.compiler.release>
|
<maven.compiler.release>17</maven.compiler.release>
|
||||||
</properties>
|
</properties>
|
||||||
|
|
||||||
<issueManagement>
|
<issueManagement>
|
||||||
|
@ -205,7 +205,7 @@
|
||||||
<artifactId>maven-compiler-plugin</artifactId>
|
<artifactId>maven-compiler-plugin</artifactId>
|
||||||
<version>3.10.1</version>
|
<version>3.10.1</version>
|
||||||
<configuration>
|
<configuration>
|
||||||
<release>11</release>
|
<release>17</release>
|
||||||
</configuration>
|
</configuration>
|
||||||
</plugin>
|
</plugin>
|
||||||
<!-- Don't generate default JAR without dependencies -->
|
<!-- Don't generate default JAR without dependencies -->
|
||||||
|
@ -274,7 +274,7 @@
|
||||||
<!-- <dontWrapJar>true</dontWrapJar> -->
|
<!-- <dontWrapJar>true</dontWrapJar> -->
|
||||||
<jre>
|
<jre>
|
||||||
<path>%PWD%/jdk</path>
|
<path>%PWD%/jdk</path>
|
||||||
<minVersion>11.0.0</minVersion>
|
<minVersion>17.0.0</minVersion>
|
||||||
</jre>
|
</jre>
|
||||||
<versionInfo>
|
<versionInfo>
|
||||||
<fileVersion>${project.version}.0.0</fileVersion>
|
<fileVersion>${project.version}.0.0</fileVersion>
|
||||||
|
|
|
@ -29,7 +29,7 @@ public class AppPreferences {
|
||||||
|
|
||||||
private final Preferences preferences;
|
private final Preferences preferences;
|
||||||
private final Locale locale;
|
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.0+"};
|
||||||
private static final Font DEFAULT_FONT = Font.getDefault();
|
private static final Font DEFAULT_FONT = Font.getDefault();
|
||||||
|
|
||||||
private AppPreferences(){
|
private AppPreferences(){
|
||||||
|
|
|
@ -352,8 +352,8 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||||
}
|
}
|
||||||
|
@ -362,8 +362,8 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
|
||||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||||
}
|
}
|
||||||
|
@ -372,8 +372,8 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -382,8 +382,8 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -392,15 +392,15 @@ public class BinToAsmPrinter {
|
||||||
int sfHw = (instructionExpression >> 22 & 1);
|
int sfHw = (instructionExpression >> 22 & 1);
|
||||||
|
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
|
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String printNOPSimplified(int instructionExpression, int offset){
|
private static String printNOPSimplified(int instructionExpression, int offset){
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String printTBZSimplified(int instructionExpression, int offset){
|
private static String printTBZSimplified(int instructionExpression, int offset){
|
||||||
|
@ -410,8 +410,8 @@ public class BinToAsmPrinter {
|
||||||
int label = offset + (instructionExpression >> 5 & 0x3fff) * 4;
|
int label = offset + (instructionExpression >> 5 & 0x3fff) * 4;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
|
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -419,15 +419,15 @@ public class BinToAsmPrinter {
|
||||||
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
getBConditionalMarker(instructionExpression & 0xf),
|
getBConditionalMarker(instructionExpression & 0xf),
|
||||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
|
||||||
}
|
}
|
||||||
private static String printImTooLazy(String name, int instructionExpression, int offset){
|
private static String printImTooLazy(String name, int instructionExpression, int offset){
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String printSUBSimplified(int instructionExpression, int offset){
|
private static String printSUBSimplified(int instructionExpression, int offset){
|
||||||
|
@ -437,8 +437,8 @@ public class BinToAsmPrinter {
|
||||||
int imm12 = instructionExpression >> 10 & 0xFFF; // unsigned only
|
int imm12 = instructionExpression >> 10 & 0xFFF; // unsigned only
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
wx, Rt, wx, Rn, imm12);
|
wx, Rt, wx, Rn, imm12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -448,8 +448,8 @@ public class BinToAsmPrinter {
|
||||||
int Rd = instructionExpression & 0x1F;
|
int Rd = instructionExpression & 0x1F;
|
||||||
|
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
sfHw, Rm, sfHw, Rd);
|
sfHw, Rm, sfHw, Rd);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -458,8 +458,8 @@ public class BinToAsmPrinter {
|
||||||
int imm = instructionExpression >> 10 & 0xFFF;
|
int imm = instructionExpression >> 10 & 0xFFF;
|
||||||
|
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
|
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -471,8 +471,8 @@ public class BinToAsmPrinter {
|
||||||
|
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
wx, Rt, wx, Rn, imm12);
|
wx, Rt, wx, Rn, imm12);
|
||||||
}
|
}
|
||||||
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
|
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
|
||||||
|
@ -482,8 +482,8 @@ public class BinToAsmPrinter {
|
||||||
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
|
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
|
||||||
|
|
||||||
return String.format(
|
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",
|
"%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, Integer.reverseBytes(instructionExpression), instructionExpression,
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
|
||||||
wx, Rt, wx, Rn, imm12);
|
wx, Rt, wx, Rn, imm12);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -494,9 +494,9 @@ public class BinToAsmPrinter {
|
||||||
int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0;
|
int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0;
|
||||||
|
|
||||||
return String.format(
|
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",
|
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,
|
Rn,
|
||||||
conditionalJumpLocation, (conditionalJumpLocation + 0x100),
|
conditionalJumpLocation, (conditionalJumpLocation + 0x100),
|
||||||
LSL);
|
LSL);
|
||||||
|
@ -527,9 +527,9 @@ public class BinToAsmPrinter {
|
||||||
}
|
}
|
||||||
|
|
||||||
return String.format(
|
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",
|
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);
|
Rn, Rm, imm6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -544,23 +544,23 @@ public class BinToAsmPrinter {
|
||||||
imm = instructionExpression >> 10 & 0x1fff;
|
imm = instructionExpression >> 10 & 0x1fff;
|
||||||
|
|
||||||
return String.format(
|
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",
|
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){
|
private static String printRetSimplified(int instructionExpression, int offset){
|
||||||
int Xn = (instructionExpression >> 5) & 0x1F;
|
int Xn = (instructionExpression >> 5) & 0x1F;
|
||||||
|
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String printUnknownSimplified(int instructionExpression, int offset){
|
private static String printUnknownSimplified(int instructionExpression, int offset){
|
||||||
return String.format(
|
return String.format(
|
||||||
"%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
|
"%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
|
||||||
offset, Integer.reverseBytes(instructionExpression), instructionExpression);
|
offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
|
||||||
}
|
}
|
||||||
|
|
||||||
private static String intAsBinString(int number) {
|
private static String intAsBinString(int number) {
|
||||||
|
|
|
@ -148,7 +148,7 @@ public class FsPatch {
|
||||||
private void findAllOffsets() throws Exception{
|
private void findAllOffsets() throws Exception{
|
||||||
this.wizard = new HeuristicFsWizard(_textSection);
|
this.wizard = new HeuristicFsWizard(_textSection);
|
||||||
String errorsAndNotes = wizard.getErrorsAndNotes();
|
String errorsAndNotes = wizard.getErrorsAndNotes();
|
||||||
if (errorsAndNotes.length() > 0)
|
if (! errorsAndNotes.isEmpty())
|
||||||
logPrinter.print(errorsAndNotes, EMsgType.WARNING);
|
logPrinter.print(errorsAndNotes, EMsgType.WARNING);
|
||||||
}
|
}
|
||||||
private void mkDirs(){
|
private void mkDirs(){
|
||||||
|
|
|
@ -39,7 +39,7 @@ import java.util.concurrent.CompletableFuture;
|
||||||
/**
|
/**
|
||||||
* GoldLeaf 0.8 processing
|
* GoldLeaf 0.8 processing
|
||||||
*/
|
*/
|
||||||
class GoldLeaf_010 extends TransferModule {
|
class GoldLeaf_ex010 extends TransferModule {
|
||||||
private boolean nspFilterForGl;
|
private boolean nspFilterForGl;
|
||||||
|
|
||||||
// CMD
|
// CMD
|
||||||
|
@ -71,7 +71,7 @@ class GoldLeaf_010 extends TransferModule {
|
||||||
|
|
||||||
private final CancellableRunnable task;
|
private final CancellableRunnable task;
|
||||||
|
|
||||||
GoldLeaf_010(DeviceHandle handler,
|
GoldLeaf_ex010(DeviceHandle handler,
|
||||||
LinkedHashMap<String, File> nspMap,
|
LinkedHashMap<String, File> nspMap,
|
||||||
CancellableRunnable task,
|
CancellableRunnable task,
|
||||||
ILogPrinter logPrinter,
|
ILogPrinter logPrinter,
|
||||||
|
@ -193,10 +193,10 @@ class GoldLeaf_010 extends TransferModule {
|
||||||
break main_loop;
|
break main_loop;
|
||||||
break;
|
break;
|
||||||
case CMD_Rename:
|
case CMD_Rename:
|
||||||
someLength1 = arrToIntLE(readByte, 12);
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
someLength2 = arrToIntLE(readByte, 16+someLength1);
|
someLength2 = arrToIntLE(readByte, 12+someLength1);
|
||||||
if (rename(new String(readByte, 16, someLength1, StandardCharsets.UTF_8),
|
if (rename(new String(readByte, 12, someLength1, StandardCharsets.UTF_8),
|
||||||
new String(readByte, 16+someLength1+4, someLength2, StandardCharsets.UTF_8)))
|
new String(readByte, 12+someLength1+4, someLength2, StandardCharsets.UTF_8)))
|
||||||
break main_loop;
|
break main_loop;
|
||||||
break;
|
break;
|
||||||
case CMD_Delete:
|
case CMD_Delete:
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019-2020 Dmitry Isaenko
|
Copyright 2019-2025 Dmitry Isaenko
|
||||||
|
|
||||||
This file is part of NS-USBloader.
|
This file is part of NS-USBloader.
|
||||||
|
|
||||||
|
@ -33,9 +33,9 @@ import java.util.*;
|
||||||
public class PFSProvider {
|
public class PFSProvider {
|
||||||
private static final byte[] PFS0 = new byte[]{0x50, 0x46, 0x53, 0x30}; // PFS0
|
private static final byte[] PFS0 = new byte[]{0x50, 0x46, 0x53, 0x30}; // PFS0
|
||||||
|
|
||||||
private String nspFileName;
|
private final String nspFileName;
|
||||||
private NCAFile[] ncaFiles;
|
private final NCAFile[] ncaFiles;
|
||||||
private long bodySize;
|
private final long bodySize;
|
||||||
private int ticketID = -1;
|
private int ticketID = -1;
|
||||||
|
|
||||||
public PFSProvider(File nspFile, ILogPrinter logPrinter) throws Exception{
|
public PFSProvider(File nspFile, ILogPrinter logPrinter) throws Exception{
|
||||||
|
@ -141,7 +141,7 @@ public class PFSProvider {
|
||||||
byte[] b = new byte[1]; // Temporary
|
byte[] b = new byte[1]; // Temporary
|
||||||
for (int i=0; i<filesCount; i++){
|
for (int i=0; i<filesCount; i++){
|
||||||
ncaFN = new ArrayList<>();
|
ncaFN = new ArrayList<>();
|
||||||
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){
|
while ((randAccessFile.read(b)) != -1){
|
||||||
if (b[0] == 0x00)
|
if (b[0] == 0x00)
|
||||||
break;
|
break;
|
||||||
|
|
|
@ -28,14 +28,20 @@ import java.io.File;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
|
|
||||||
public abstract class TransferModule {
|
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<String, File> nspMap;
|
protected EFileStatus status = EFileStatus.UNKNOWN;
|
||||||
ILogPrinter logPrinter;
|
|
||||||
DeviceHandle handlerNS;
|
|
||||||
CancellableRunnable task;
|
|
||||||
|
|
||||||
TransferModule(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter printer){
|
protected LinkedHashMap<String, File> nspMap;
|
||||||
|
protected ILogPrinter logPrinter;
|
||||||
|
protected DeviceHandle handlerNS;
|
||||||
|
protected CancellableRunnable task;
|
||||||
|
|
||||||
|
protected TransferModule(DeviceHandle handler,
|
||||||
|
LinkedHashMap<String, File> nspMap,
|
||||||
|
CancellableRunnable task,
|
||||||
|
ILogPrinter printer){
|
||||||
this.handlerNS = handler;
|
this.handlerNS = handler;
|
||||||
this.nspMap = nspMap;
|
this.nspMap = nspMap;
|
||||||
this.task = task;
|
this.task = task;
|
||||||
|
@ -78,7 +84,7 @@ public abstract class TransferModule {
|
||||||
}
|
}
|
||||||
public EFileStatus getStatus(){ return status; }
|
public EFileStatus getStatus(){ return status; }
|
||||||
|
|
||||||
void print(String message, EMsgType type){
|
protected void print(String message, EMsgType type){
|
||||||
try {
|
try {
|
||||||
logPrinter.print(message, type);
|
logPrinter.print(message, type);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2019-2020 Dmitry Isaenko
|
Copyright 2019-2025 Dmitry Isaenko
|
||||||
|
|
||||||
This file is part of NS-USBloader.
|
This file is part of NS-USBloader.
|
||||||
|
|
||||||
|
@ -24,6 +24,8 @@ import nsusbloader.ModelControllers.Log;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
import nsusbloader.NSLDataTypes.EModule;
|
import nsusbloader.NSLDataTypes.EModule;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import nsusbloader.com.usb.gl.GoldLeaf_010;
|
||||||
|
import nsusbloader.com.usb.gl.GoldLeaf_111;
|
||||||
import org.usb4java.*;
|
import org.usb4java.*;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -66,7 +68,10 @@ public class UsbCommunications extends CancellableRunnable {
|
||||||
case "TinFoil":
|
case "TinFoil":
|
||||||
module = new TinFoil(handler, nspMap, this, logPrinter);
|
module = new TinFoil(handler, nspMap, this, logPrinter);
|
||||||
break;
|
break;
|
||||||
case "GoldLeafv0.10+":
|
case "GoldLeafv1.1.0+":
|
||||||
|
module = new GoldLeaf_111(handler, nspMap, this, logPrinter, nspFilterForGl);
|
||||||
|
break;
|
||||||
|
case "GoldLeafv0.10-1.0.0":
|
||||||
module = new GoldLeaf_010(handler, nspMap, this, logPrinter, nspFilterForGl);
|
module = new GoldLeaf_010(handler, nspMap, this, logPrinter, nspFilterForGl);
|
||||||
break;
|
break;
|
||||||
case "GoldLeafv0.8-0.9":
|
case "GoldLeafv0.8-0.9":
|
||||||
|
|
53
src/main/java/nsusbloader/com/usb/gl/Converters.java
Normal file
53
src/main/java/nsusbloader/com/usb/gl/Converters.java
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
}
|
899
src/main/java/nsusbloader/com/usb/gl/GoldLeaf_010.java
Normal file
899
src/main/java/nsusbloader/com/usb/gl/GoldLeaf_010.java
Normal file
|
@ -0,0 +1,899 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package nsusbloader.com.usb.gl;
|
||||||
|
|
||||||
|
import javafx.application.Platform;
|
||||||
|
import javafx.stage.FileChooser;
|
||||||
|
import libKonogonka.RainbowDump;
|
||||||
|
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 nsusbloader.com.usb.UsbErrorCodes;
|
||||||
|
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<String, BufferedOutputStream> writeFilesMap = new HashMap<>();
|
||||||
|
private long virtDriveSize;
|
||||||
|
private final HashMap<String, Long> splitFileSize = new HashMap<>();
|
||||||
|
|
||||||
|
private final boolean isWindows = System.getProperty("os.name").contains("Windows");
|
||||||
|
private final String homePath = System.getProperty("user.home");
|
||||||
|
// For using in CMD_SelectFile with SPEC:/ prefix
|
||||||
|
protected File selectedFile;
|
||||||
|
|
||||||
|
public GoldLeaf_010(DeviceHandle handler,
|
||||||
|
LinkedHashMap<String, File> nspMap,
|
||||||
|
CancellableRunnable task,
|
||||||
|
ILogPrinter logPrinter,
|
||||||
|
boolean nspFilter) {
|
||||||
|
super(handler, nspMap, task, logPrinter);
|
||||||
|
|
||||||
|
this.nspFilter = nspFilter;
|
||||||
|
|
||||||
|
print("=========== GoldLeaf v0.10-1.0.0 ===========\n\t" +
|
||||||
|
"VIRT:/ equals files added into the application\n\t" +
|
||||||
|
"HOME:/ equals " + homePath, EMsgType.INFO);
|
||||||
|
|
||||||
|
// 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
|
||||||
|
main_loop:
|
||||||
|
while (true) { // Till user interrupted process.
|
||||||
|
int someLength1, someLength2;
|
||||||
|
var readByte = readGL();
|
||||||
|
|
||||||
|
if (readByte == null) // Issue @ readFromUsbGL method
|
||||||
|
return;
|
||||||
|
|
||||||
|
//RainbowDump.hexDumpUTF8(readByte); // DEBUG
|
||||||
|
System.out.println("\t→ "+ GoldleafCmd.get(readByte[4]));
|
||||||
|
|
||||||
|
if (notGLCI(readByte))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (GoldleafCmd.get(readByte[4])) {
|
||||||
|
case GetDriveCount:
|
||||||
|
if (getDriveCount())
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetDriveInfo:
|
||||||
|
if (getDriveInfo(arrToIntLE(readByte,8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetSpecialPathCount:
|
||||||
|
if (getSpecialPathCount())
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetSpecialPath:
|
||||||
|
if (getSpecialPath(arrToIntLE(readByte,8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetDirectoryCount:
|
||||||
|
RainbowDump.hexDumpUTF8(readByte); // DEBUG
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), true))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetFileCount:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), false))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetDirectory:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case GetFile:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case StatPath:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case Rename:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
someLength2 = arrToIntLE(readByte, 12+someLength1);
|
||||||
|
if (rename(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), // 8+4=12
|
||||||
|
new String(readByte, 12+someLength1+4, someLength2, StandardCharsets.UTF_8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case Delete:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (delete(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case Create:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (create(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), readByte[8]))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case 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 WriteFile:
|
||||||
|
someLength1 = arrToIntLE(readByte, 8);
|
||||||
|
if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case SelectFile:
|
||||||
|
if (selectFile())
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case StartFile:
|
||||||
|
case EndFile:
|
||||||
|
if (startOrEndFile())
|
||||||
|
break main_loop;
|
||||||
|
break;
|
||||||
|
case CMD_UNKNOWN:
|
||||||
|
default:
|
||||||
|
writeGL_FAIL(EXCEPTION_CAUGHT, "GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Close (and flush) all opened streams.
|
||||||
|
for (var bufferedOutputStream: writeFilesMap.values()){
|
||||||
|
try{
|
||||||
|
bufferedOutputStream.close();
|
||||||
|
} catch (IOException | NullPointerException ignored){}
|
||||||
|
}
|
||||||
|
closeOpenedReadFilesGl();
|
||||||
|
}
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private boolean startOrEndFile(){
|
||||||
|
return writeGL_PASS("GL Handle 'StartFile' command");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle GetDriveCount
|
||||||
|
* 2 drives declared in current implementation
|
||||||
|
* @return true - failed, false - passed
|
||||||
|
*/
|
||||||
|
private boolean getDriveCount(){
|
||||||
|
return writeGL_PASS(intToArrLE(2),"GL Handle 'ListDrives' command");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle GetDriveInfo
|
||||||
|
* @return true - failed, false - passed
|
||||||
|
*/
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private boolean getSpecialPathCount(){
|
||||||
|
return writeGL_PASS(intToArrLE(0), "GL Handle 'SpecialPathCount' command");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle SpecialPath
|
||||||
|
* @return true - failed, false - passed
|
||||||
|
* */
|
||||||
|
private boolean getSpecialPath(int specialPathNo){
|
||||||
|
return writeGL_FAIL(INVALID_INDEX, "GL Handle 'SpecialPath' command [not supported]");
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Handle GetDirectoryCount & GetFileCount
|
||||||
|
* @return true - failed, false - passed
|
||||||
|
* */
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private boolean getDirectory(String dirName, int subDirNo){
|
||||||
|
if (dirName.startsWith("HOME:/")) {
|
||||||
|
dirName = decodeGlPath(dirName);
|
||||||
|
|
||||||
|
var command = new ArrayList<byte[]>();
|
||||||
|
|
||||||
|
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
|
||||||
|
* */
|
||||||
|
private boolean getFile(String glDirName, int subDirNo){
|
||||||
|
var command = new LinkedList<byte[]>();
|
||||||
|
|
||||||
|
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
|
||||||
|
* */
|
||||||
|
private boolean statPath(String glFileName){
|
||||||
|
var command = new ArrayList<byte[]>();
|
||||||
|
|
||||||
|
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
|
||||||
|
* */
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private 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("^.*?:/", "");
|
||||||
|
System.out.println(fileName+" readFile "+glFileName+"\t"+offset+"\t"+size+"\n");
|
||||||
|
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
|
||||||
|
System.out.println("SENT 1 -");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
System.out.println("SENT 1");
|
||||||
|
if (writeToUsb(chunk)) { // Bypassing bytes we read total // FIXME: move failure message into method
|
||||||
|
print("GL Handle 'ReadFile' command", EMsgType.FAIL);
|
||||||
|
System.out.println("SENT 2 -");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
System.out.println("SENT 2");
|
||||||
|
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
|
||||||
|
* */
|
||||||
|
private 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
|
||||||
|
* */
|
||||||
|
private 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 */
|
||||||
|
/*----------------------------------------------------*/
|
||||||
|
|
||||||
|
private 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<byte[]> 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;
|
||||||
|
}
|
||||||
|
}
|
171
src/main/java/nsusbloader/com/usb/gl/GoldLeaf_111.java
Normal file
171
src/main/java/nsusbloader/com/usb/gl/GoldLeaf_111.java
Normal file
|
@ -0,0 +1,171 @@
|
||||||
|
/*
|
||||||
|
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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package nsusbloader.com.usb.gl;
|
||||||
|
|
||||||
|
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.util.*;
|
||||||
|
|
||||||
|
import static nsusbloader.com.usb.gl.Converters.longToArrLE;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* GoldLeaf 1.1.1 processing
|
||||||
|
*/
|
||||||
|
public class GoldLeaf_111 extends GoldLeaf_010{
|
||||||
|
public GoldLeaf_111(DeviceHandle handler, LinkedHashMap<String, File> nspMap,
|
||||||
|
CancellableRunnable task,
|
||||||
|
ILogPrinter logPrinter,
|
||||||
|
boolean nspFilter) {
|
||||||
|
super(handler, nspMap, task, logPrinter, nspFilter);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected boolean readFile(String glFileName, long offset, long size) {
|
||||||
|
var fileName = glFileName.replaceFirst("^.*?:/", "");
|
||||||
|
System.out.println(fileName+" readFile "+glFileName+"\t"+offset+"\t"+size+"\n");
|
||||||
|
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
|
||||||
|
System.out.println("+SENT 1 -");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
System.out.println("+SENT 1");
|
||||||
|
if (writeToUsbV100(chunk)) { // Bypassing bytes we read total // FIXME: move failure message into method
|
||||||
|
print("GL Handle 'ReadFile' command", EMsgType.FAIL);
|
||||||
|
System.out.println("+SENT 2 -");
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
System.out.println("+SENT 2");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
catch (Exception ioe){
|
||||||
|
closeOpenedReadFilesGl();
|
||||||
|
return writeGL_FAIL(EXCEPTION_CAUGHT, "GL Handle 'ReadFile' transfer chain\n\t"+ioe.getMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean writeToUsbV100(byte[] message) {
|
||||||
|
var writeBufTransferred = IntBuffer.allocate(1);
|
||||||
|
|
||||||
|
while (! task.isCancelled()) {
|
||||||
|
int result = LibUsb.bulkTransfer(handlerNS,
|
||||||
|
OUT_EP,
|
||||||
|
ByteBuffer.allocateDirect(message.length)
|
||||||
|
.order(ByteOrder.LITTLE_ENDIAN)
|
||||||
|
.put(message),
|
||||||
|
writeBufTransferred,
|
||||||
|
1000);
|
||||||
|
|
||||||
|
switch (result){
|
||||||
|
case LibUsb.SUCCESS:
|
||||||
|
if (writeBufTransferred.get() == message.length)
|
||||||
|
return false;
|
||||||
|
print("GL Data transfer issue [write]\n Requested: " +
|
||||||
|
message.length +
|
||||||
|
"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
case LibUsb.ERROR_TIMEOUT:
|
||||||
|
print("GL Data transfer issue [write]", EMsgType.WARNING);
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
54
src/main/java/nsusbloader/com/usb/gl/GoldleafCmd.java
Normal file
54
src/main/java/nsusbloader/com/usb/gl/GoldleafCmd.java
Normal file
|
@ -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 <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -8,7 +8,7 @@ import org.junit.jupiter.api.*;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@Disabled
|
//@Disabled
|
||||||
public class EsIntegrationTest {
|
public class EsIntegrationTest {
|
||||||
static String pathToFirmware;
|
static String pathToFirmware;
|
||||||
static String pathToFirmwares;
|
static String pathToFirmwares;
|
||||||
|
@ -22,18 +22,17 @@ public class EsIntegrationTest {
|
||||||
pathToKeysFile = environment.getProdkeysLocation();
|
pathToKeysFile = environment.getProdkeysLocation();
|
||||||
saveTo = environment.getSaveToLocation() + File.separator + "ES_LPR";
|
saveTo = environment.getSaveToLocation() + File.separator + "ES_LPR";
|
||||||
pathToFirmwares = environment.getFirmwaresLocation();
|
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")
|
@DisplayName("ES Integration validation - everything")
|
||||||
@Test
|
@Test
|
||||||
void makeEss() throws Exception{
|
void makeEss() throws Exception{
|
||||||
File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
|
File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
|
||||||
return s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*");
|
s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*"));
|
||||||
//return s.matches("^Firmware 10.0.1.*");
|
|
||||||
});
|
|
||||||
assert fwDirs != null;
|
assert fwDirs != null;
|
||||||
Arrays.sort(fwDirs);
|
Arrays.sort(fwDirs);
|
||||||
|
Arrays.stream(fwDirs).forEach(System.out::println);
|
||||||
for (File dir : fwDirs){
|
for (File dir : fwDirs){
|
||||||
EsPatchMaker esPatchMaker = new EsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
|
EsPatchMaker esPatchMaker = new EsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
|
||||||
Thread workThread = new Thread(esPatchMaker);
|
Thread workThread = new Thread(esPatchMaker);
|
||||||
|
@ -42,6 +41,7 @@ public class EsIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Disabled
|
||||||
@DisplayName("ES Integration validation - one particular firmware")
|
@DisplayName("ES Integration validation - one particular firmware")
|
||||||
@Test
|
@Test
|
||||||
void makeEs() throws Exception{
|
void makeEs() throws Exception{
|
||||||
|
|
|
@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
|
|
||||||
@Disabled
|
//@Disabled
|
||||||
public class FsIntegrationTest {
|
public class FsIntegrationTest {
|
||||||
static String pathToFirmware;
|
static String pathToFirmware;
|
||||||
static String pathToFirmwares;
|
static String pathToFirmwares;
|
||||||
|
@ -25,19 +25,17 @@ public class FsIntegrationTest {
|
||||||
pathToKeysFile = environment.getProdkeysLocation();
|
pathToKeysFile = environment.getProdkeysLocation();
|
||||||
saveTo = environment.getSaveToLocation() + File.separator + "FS_LPR";
|
saveTo = environment.getSaveToLocation() + File.separator + "FS_LPR";
|
||||||
pathToFirmwares = environment.getFirmwaresLocation();
|
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")
|
@DisplayName("FS Integration validation - everything")
|
||||||
@Test
|
@Test
|
||||||
void makeFss() throws Exception{
|
void makeFss() throws Exception{
|
||||||
File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
|
File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
|
||||||
return (s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
|
s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
|
||||||
//return s.matches("^Firmware 10.0.1.*");
|
|
||||||
});
|
|
||||||
assert fwDirs != null;
|
assert fwDirs != null;
|
||||||
Arrays.sort(fwDirs);
|
Arrays.sort(fwDirs);
|
||||||
|
Arrays.stream(fwDirs).forEach(System.out::println);
|
||||||
for (File dir : fwDirs){
|
for (File dir : fwDirs){
|
||||||
System.out.println("\n\t\t\t"+dir.getName());
|
System.out.println("\n\t\t\t"+dir.getName());
|
||||||
FsPatchMaker fsPatchMaker = new FsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
|
FsPatchMaker fsPatchMaker = new FsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
|
||||||
|
@ -47,6 +45,7 @@ public class FsIntegrationTest {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Disabled
|
||||||
@DisplayName("FS Integration validation - one particular firmware")
|
@DisplayName("FS Integration validation - one particular firmware")
|
||||||
@Test
|
@Test
|
||||||
void makeFs() throws Exception{
|
void makeFs() throws Exception{
|
||||||
|
|
|
@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
|
||||||
@Disabled
|
//@Disabled
|
||||||
public class LoaderIntegrationTest {
|
public class LoaderIntegrationTest {
|
||||||
static String pathToAtmo;
|
static String pathToAtmo;
|
||||||
static String saveTo;
|
static String saveTo;
|
||||||
|
|
Loading…
Reference in a new issue