Compare commits
	
		
			No commits in common. "db7855765fc97a5e8c71c11f591c784e6ba6818f" and "6d243a2be1a015cfc4b6cd38bb0dd4fe47903c95" have entirely different histories.
		
	
	
		
			db7855765f
			...
			6d243a2be1
		
	
		
					 20 changed files with 1209 additions and 1280 deletions
				
			
		| 
						 | 
				
			
			@ -1,7 +1,7 @@
 | 
			
		|||
steps:
 | 
			
		||||
  - name: test-standard
 | 
			
		||||
    when:
 | 
			
		||||
      event: [tag, push, manual]
 | 
			
		||||
      event: [tag, push]
 | 
			
		||||
    image: maven:3-openjdk-17
 | 
			
		||||
    commands:
 | 
			
		||||
      - mvn -B -DskipTests clean package
 | 
			
		||||
| 
						 | 
				
			
			@ -14,7 +14,7 @@ steps:
 | 
			
		|||
 | 
			
		||||
  - name: make-windows-installer
 | 
			
		||||
    when:
 | 
			
		||||
      event: [tag, push, manual]
 | 
			
		||||
      event: [tag, push]
 | 
			
		||||
    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, manual]
 | 
			
		||||
      event: [tag, push]
 | 
			
		||||
    image: maven:3-openjdk-17
 | 
			
		||||
    commands:
 | 
			
		||||
      - . ./.make_legacy
 | 
			
		||||
| 
						 | 
				
			
			@ -44,7 +44,7 @@ steps:
 | 
			
		|||
 | 
			
		||||
  - name: make-legacy-windows-installer
 | 
			
		||||
    when:
 | 
			
		||||
      event: [tag, push, manual]
 | 
			
		||||
      event: [tag, push]
 | 
			
		||||
    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, manual]
 | 
			
		||||
      event: [tag, push]
 | 
			
		||||
    image: maven:3-openjdk-17
 | 
			
		||||
    commands:
 | 
			
		||||
      - . ./.make_m1
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -67,7 +67,7 @@ Sometimes I add new posts about this project [on my blog page](https://developer
 | 
			
		|||
 | 
			
		||||
### System requirements
 | 
			
		||||
 | 
			
		||||
- JDK 17 for macOS and Linux
 | 
			
		||||
- JDK 11 for macOS and Linux
 | 
			
		||||
- libusb, if you have a Mac with Apple Silicon (install via `brew install libusb`)
 | 
			
		||||
 | 
			
		||||
### Supported Goldleaf versions
 | 
			
		||||
| 
						 | 
				
			
			@ -78,9 +78,7 @@ 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 - 1.0.0    | v6.0+                |
 | 
			
		||||
| v1.1.0           | none                 |
 | 
			
		||||
| v1.1.1           | v7.3+                |
 | 
			
		||||
| v0.10            | v6.0+                |
 | 
			
		||||
 | 
			
		||||
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>
 | 
			
		||||
        <maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
 | 
			
		||||
        <javafx.version>19.0.2.1</javafx.version>
 | 
			
		||||
        <maven.compiler.release>17</maven.compiler.release>
 | 
			
		||||
        <maven.compiler.release>11</maven.compiler.release>
 | 
			
		||||
    </properties>
 | 
			
		||||
 | 
			
		||||
    <issueManagement>
 | 
			
		||||
| 
						 | 
				
			
			@ -205,7 +205,7 @@
 | 
			
		|||
                <artifactId>maven-compiler-plugin</artifactId>
 | 
			
		||||
                <version>3.10.1</version>
 | 
			
		||||
                <configuration>
 | 
			
		||||
                    <release>17</release>
 | 
			
		||||
                    <release>11</release>
 | 
			
		||||
                </configuration>
 | 
			
		||||
            </plugin>
 | 
			
		||||
            <!-- Don't generate default JAR without dependencies -->
 | 
			
		||||
| 
						 | 
				
			
			@ -274,7 +274,7 @@
 | 
			
		|||
                            <!-- <dontWrapJar>true</dontWrapJar> -->
 | 
			
		||||
                            <jre>
 | 
			
		||||
                                <path>%PWD%/jdk</path>
 | 
			
		||||
                                <minVersion>17.0.0</minVersion>
 | 
			
		||||
                                <minVersion>11.0.0</minVersion>
 | 
			
		||||
                            </jre>
 | 
			
		||||
                            <versionInfo>
 | 
			
		||||
                                <fileVersion>${project.version}.0.0</fileVersion>
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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-1.0.0", "v1.1.1"};
 | 
			
		||||
    public static final String[] GOLDLEAF_SUPPORTED_VERSIONS = {"v0.5", "v0.7.x", "v0.8-0.9", "v0.10+"};
 | 
			
		||||
    private static final Font DEFAULT_FONT = Font.getDefault();
 | 
			
		||||
 | 
			
		||||
    private AppPreferences(){
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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")){
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -352,8 +352,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CBZ         " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                (instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
 | 
			
		||||
                conditionalJumpLocation, (conditionalJumpLocation + 0x100));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -362,8 +362,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CBNZ        " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                (instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
 | 
			
		||||
                conditionalJumpLocation, (conditionalJumpLocation + 0x100));
 | 
			
		||||
    }
 | 
			
		||||
| 
						 | 
				
			
			@ -372,8 +372,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   B           " +  ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -382,8 +382,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   BL          " +  ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -392,15 +392,15 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int sfHw = (instructionExpression >> 22 & 1);
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   MOV         " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                (sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String printNOPSimplified(int instructionExpression, int offset){
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   NOP           " + ANSI_RESET + "\n",
 | 
			
		||||
                offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   NOP           " + ANSI_RESET + "\n",
 | 
			
		||||
                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(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   TBZ         " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                (xwSelector == 0) ? "w" : "x", Rt, imm, label);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -419,15 +419,15 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   B.%s        " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                getBConditionalMarker(instructionExpression & 0xf),
 | 
			
		||||
                conditionalJumpLocation, (conditionalJumpLocation + 0x100));
 | 
			
		||||
    }
 | 
			
		||||
    private static String printImTooLazy(String name, int instructionExpression, int offset){
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   "+name+"           . . . \n"+ ANSI_RESET,
 | 
			
		||||
                offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   "+name+"           . . . \n"+ ANSI_RESET,
 | 
			
		||||
                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(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   SUB (imm)   " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                wx, Rt, wx, Rn, imm12);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -448,8 +448,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int Rd = instructionExpression & 0x1F;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   MOV (reg)   " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                sfHw, Rm, sfHw, Rd);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -458,8 +458,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int imm = instructionExpression >> 10 & 0xFFF;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CMN         " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                (instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -471,8 +471,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   LDR(imm)    " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + "     (note: unsigned offset)\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                wx, Rt, wx, Rn, imm12);
 | 
			
		||||
    }
 | 
			
		||||
    private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
 | 
			
		||||
| 
						 | 
				
			
			@ -482,8 +482,8 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%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,
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   LDRB(imm)   " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + "     (note: unsigned offset)\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                wx, Rt, wx, Rn, imm12);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -494,9 +494,9 @@ public class BinToAsmPrinter {
 | 
			
		|||
        int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CMP         " + ANSI_GREEN + sf + "%d," +
 | 
			
		||||
                "%05x "+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+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                Rn,
 | 
			
		||||
                conditionalJumpLocation, (conditionalJumpLocation + 0x100),
 | 
			
		||||
                LSL);
 | 
			
		||||
| 
						 | 
				
			
			@ -527,9 +527,9 @@ public class BinToAsmPrinter {
 | 
			
		|||
        }
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CMP (sr)    " + ANSI_GREEN + sf + "%d," +
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   CMP (sr)    " + ANSI_GREEN + sf + "%d," +
 | 
			
		||||
                        ANSI_BLUE + sf + "%d " + ANSI_BLUE + LSLStr + ANSI_PURPLE + " %d" + ANSI_RESET + "\n",
 | 
			
		||||
                offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression,
 | 
			
		||||
                Rn, Rm, imm6);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -544,23 +544,23 @@ public class BinToAsmPrinter {
 | 
			
		|||
            imm = instructionExpression >> 10 & 0x1fff;
 | 
			
		||||
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   AND         " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   AND         " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
 | 
			
		||||
                        sf + "%d" + ANSI_PURPLE + " # ??? 0b%s " + ANSI_RESET + "\n",
 | 
			
		||||
                offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm));
 | 
			
		||||
                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(
 | 
			
		||||
                "%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);
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   RET        " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String printUnknownSimplified(int instructionExpression, int offset){
 | 
			
		||||
        return String.format(
 | 
			
		||||
                "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   ???          0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
 | 
			
		||||
                offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
 | 
			
		||||
                "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + "   ???          0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
 | 
			
		||||
                offset, Integer.reverseBytes(instructionExpression), instructionExpression);
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    private static String intAsBinString(int number) {
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -148,7 +148,7 @@ public class FsPatch {
 | 
			
		|||
    private void findAllOffsets() throws Exception{
 | 
			
		||||
        this.wizard = new HeuristicFsWizard(_textSection);
 | 
			
		||||
        String errorsAndNotes = wizard.getErrorsAndNotes();
 | 
			
		||||
        if (! errorsAndNotes.isEmpty())
 | 
			
		||||
        if (errorsAndNotes.length() > 0)
 | 
			
		||||
            logPrinter.print(errorsAndNotes, EMsgType.WARNING);
 | 
			
		||||
    }
 | 
			
		||||
    private void mkDirs(){
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -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);
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
							
								
								
									
										1130
									
								
								src/main/java/nsusbloader/com/usb/GoldLeaf_010.java
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1130
									
								
								src/main/java/nsusbloader/com/usb/GoldLeaf_010.java
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
    Copyright 2019-2025 Dmitry Isaenko
 | 
			
		||||
    Copyright 2019-2020 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 final String nspFileName;
 | 
			
		||||
    private final NCAFile[] ncaFiles;
 | 
			
		||||
    private final long bodySize;
 | 
			
		||||
    private String nspFileName;
 | 
			
		||||
    private NCAFile[] ncaFiles;
 | 
			
		||||
    private 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<filesCount; i++){
 | 
			
		||||
            ncaFN = new ArrayList<>();
 | 
			
		||||
            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
 | 
			
		||||
            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
 | 
			
		||||
            while ((randAccessFile.read(b)) != -1){
 | 
			
		||||
                if (b[0] == 0x00)
 | 
			
		||||
                    break;
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -28,20 +28,14 @@ import java.io.File;
 | 
			
		|||
import java.util.*;
 | 
			
		||||
 | 
			
		||||
public abstract class TransferModule {
 | 
			
		||||
    protected static final byte IN_EP = (byte) 0x81;
 | 
			
		||||
    protected static final byte OUT_EP = (byte) 0x01;
 | 
			
		||||
    EFileStatus status = EFileStatus.UNKNOWN;
 | 
			
		||||
 | 
			
		||||
    protected EFileStatus status = EFileStatus.UNKNOWN;
 | 
			
		||||
    LinkedHashMap<String, File> nspMap;
 | 
			
		||||
    ILogPrinter logPrinter;
 | 
			
		||||
    DeviceHandle handlerNS;
 | 
			
		||||
    CancellableRunnable task;
 | 
			
		||||
 | 
			
		||||
    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){
 | 
			
		||||
    TransferModule(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter printer){
 | 
			
		||||
        this.handlerNS = handler;
 | 
			
		||||
        this.nspMap = nspMap;
 | 
			
		||||
        this.task = task;
 | 
			
		||||
| 
						 | 
				
			
			@ -84,7 +78,7 @@ public abstract class TransferModule {
 | 
			
		|||
    }
 | 
			
		||||
    public EFileStatus getStatus(){ return status; }
 | 
			
		||||
 | 
			
		||||
    protected void print(String message, EMsgType type){
 | 
			
		||||
    void print(String message, EMsgType type){
 | 
			
		||||
        try {
 | 
			
		||||
            logPrinter.print(message, type);
 | 
			
		||||
        }
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,5 +1,5 @@
 | 
			
		|||
/*
 | 
			
		||||
    Copyright 2019-2025 Dmitry Isaenko
 | 
			
		||||
    Copyright 2019-2020 Dmitry Isaenko
 | 
			
		||||
 | 
			
		||||
    This file is part of NS-USBloader.
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			@ -24,8 +24,6 @@ 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.*;
 | 
			
		||||
| 
						 | 
				
			
			@ -68,16 +66,13 @@ public class UsbCommunications extends CancellableRunnable {
 | 
			
		|||
            case "TinFoil":
 | 
			
		||||
                module = new TinFoil(handler, nspMap, this, logPrinter);
 | 
			
		||||
                break;
 | 
			
		||||
            case "GoldLeaf v1.1.1":
 | 
			
		||||
                module = new GoldLeaf_111(handler, nspMap, this, logPrinter, nspFilterForGl);
 | 
			
		||||
                break;
 | 
			
		||||
            case "GoldLeaf v0.10-1.0.0":
 | 
			
		||||
            case "GoldLeafv0.10+":
 | 
			
		||||
                module = new GoldLeaf_010(handler, nspMap, this, logPrinter, nspFilterForGl);
 | 
			
		||||
                break;
 | 
			
		||||
            case "GoldLeaf v0.8-0.9":
 | 
			
		||||
            case "GoldLeafv0.8-0.9":
 | 
			
		||||
                module = new GoldLeaf_08(handler, nspMap, this, logPrinter, nspFilterForGl);
 | 
			
		||||
                break;
 | 
			
		||||
            case "GoldLeaf v0.7.x":
 | 
			
		||||
            case "GoldLeafv0.7.x":
 | 
			
		||||
                module = new GoldLeaf_07(handler, nspMap, this, logPrinter, nspFilterForGl);
 | 
			
		||||
                break;
 | 
			
		||||
            default:
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -1,53 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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);
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,23 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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 interface GlString {
 | 
			
		||||
    int length();
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,44 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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.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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,897 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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 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<String, BufferedOutputStream> writeFilesMap = new HashMap<>();
 | 
			
		||||
    protected long virtDriveSize;
 | 
			
		||||
    private final HashMap<String, Long> 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<String, File> 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<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
 | 
			
		||||
     * */
 | 
			
		||||
    protected 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
 | 
			
		||||
     * */
 | 
			
		||||
    protected 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
 | 
			
		||||
     * */
 | 
			
		||||
    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<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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,118 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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 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<String, File> 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");
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -1,54 +0,0 @@
 | 
			
		|||
/*
 | 
			
		||||
    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;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
| 
						 | 
				
			
			@ -22,17 +22,18 @@ public class EsIntegrationTest {
 | 
			
		|||
        pathToKeysFile = environment.getProdkeysLocation();
 | 
			
		||||
        saveTo = environment.getSaveToLocation() + File.separator + "ES_LPR";
 | 
			
		||||
        pathToFirmwares = environment.getFirmwaresLocation();
 | 
			
		||||
        pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 19.0.1";
 | 
			
		||||
        pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @DisplayName("ES Integration validation - everything")
 | 
			
		||||
    @Test
 | 
			
		||||
    void makeEss() throws Exception{
 | 
			
		||||
        File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
 | 
			
		||||
                s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*"));
 | 
			
		||||
        File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
 | 
			
		||||
            return s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*");
 | 
			
		||||
            //return s.matches("^Firmware 10.0.1.*");
 | 
			
		||||
        });
 | 
			
		||||
        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);
 | 
			
		||||
| 
						 | 
				
			
			@ -41,7 +42,6 @@ public class EsIntegrationTest {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Disabled
 | 
			
		||||
    @DisplayName("ES Integration validation - one particular firmware")
 | 
			
		||||
    @Test
 | 
			
		||||
    void makeEs() throws Exception{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -25,17 +25,19 @@ public class FsIntegrationTest {
 | 
			
		|||
        pathToKeysFile = environment.getProdkeysLocation();
 | 
			
		||||
        saveTo = environment.getSaveToLocation() + File.separator + "FS_LPR";
 | 
			
		||||
        pathToFirmwares = environment.getFirmwaresLocation();
 | 
			
		||||
        pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 15.0.0";
 | 
			
		||||
        pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @DisplayName("FS Integration validation - everything")
 | 
			
		||||
    @Test
 | 
			
		||||
    void makeFss() throws Exception{
 | 
			
		||||
        File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
 | 
			
		||||
                s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
 | 
			
		||||
        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.*");
 | 
			
		||||
        });
 | 
			
		||||
        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);
 | 
			
		||||
| 
						 | 
				
			
			@ -45,7 +47,6 @@ public class FsIntegrationTest {
 | 
			
		|||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    @Disabled
 | 
			
		||||
    @DisplayName("FS Integration validation - one particular firmware")
 | 
			
		||||
    @Test
 | 
			
		||||
    void makeFs() throws Exception{
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in a new issue