diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java
index ceb9542..e4ae860 100644
--- a/src/main/java/nsusbloader/AppPreferences.java
+++ b/src/main/java/nsusbloader/AppPreferences.java
@@ -29,7 +29,7 @@ public class AppPreferences {
private final Preferences preferences;
private final Locale locale;
- public static final String[] GOLDLEAF_SUPPORTED_VERSIONS = {"v0.5", "v0.7.x", "v0.8-0.9", "v0.10+"};
+ public static final String[] GOLDLEAF_SUPPORTED_VERSIONS = {"v0.5", "v0.7.x", "v0.8-0.9", "v0.10-1.1.0", "v1.1.1+"};
private static final Font DEFAULT_FONT = Font.getDefault();
private AppPreferences(){
diff --git a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
index 0109258..03ad1c4 100644
--- a/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
+++ b/src/main/java/nsusbloader/Utilities/patches/BinToAsmPrinter.java
@@ -352,8 +352,8 @@ public class BinToAsmPrinter {
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
@@ -362,8 +362,8 @@ public class BinToAsmPrinter {
int conditionalJumpLocation = ((instructionExpression >> 5 & 0x7FFFF) * 4 + offset) & 0xfffff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CBNZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (" + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", (instructionExpression & 0b11111),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
@@ -372,8 +372,8 @@ public class BinToAsmPrinter {
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
@@ -382,8 +382,8 @@ public class BinToAsmPrinter {
int conditionalJumpLocationPatch = ((instructionExpression & 0x3ffffff) * 4 + offset) & 0xfffff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " BL " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " BL " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
conditionalJumpLocationPatch, (conditionalJumpLocationPatch + 0x100));
}
@@ -392,15 +392,15 @@ public class BinToAsmPrinter {
int sfHw = (instructionExpression >> 22 & 1);
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(sfHw == 0) ? "w" : "x", (instructionExpression & 0b11111), imm16);
}
private static String printNOPSimplified(int instructionExpression, int offset){
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " NOP " + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printTBZSimplified(int instructionExpression, int offset){
@@ -410,8 +410,8 @@ public class BinToAsmPrinter {
int label = offset + (instructionExpression >> 5 & 0x3fff) * 4;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " TBZ " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ", " + ANSI_PURPLE + "%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(xwSelector == 0) ? "w" : "x", Rt, imm, label);
}
@@ -419,15 +419,15 @@ public class BinToAsmPrinter {
int conditionalJumpLocation = ((instructionExpression >> 4 & 0b1111111111111111111) * 4 + offset) & 0xfffff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " B.%s " + ANSI_BLUE + "#0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ")\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
getBConditionalMarker(instructionExpression & 0xf),
conditionalJumpLocation, (conditionalJumpLocation + 0x100));
}
private static String printImTooLazy(String name, int instructionExpression, int offset){
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
- offset, Integer.reverseBytes(instructionExpression), instructionExpression);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " "+name+" . . . \n"+ ANSI_RESET,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String printSUBSimplified(int instructionExpression, int offset){
@@ -437,8 +437,8 @@ public class BinToAsmPrinter {
int imm12 = instructionExpression >> 10 & 0xFFF; // unsigned only
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " SUB (imm) " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " SUB (imm) " + ANSI_GREEN + "%s%d, " + ANSI_BLUE + "%s%d, #0x%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
@@ -448,8 +448,8 @@ public class BinToAsmPrinter {
int Rd = instructionExpression & 0x1F;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " MOV (reg) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "%s%d" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
sfHw, Rm, sfHw, Rd);
}
@@ -458,8 +458,8 @@ public class BinToAsmPrinter {
int imm = instructionExpression >> 10 & 0xFFF;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMN " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "#0x%x" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
(instructionExpression >> 31 == 0) ? "w" : "x", Rn, imm);
}
@@ -471,8 +471,8 @@ public class BinToAsmPrinter {
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDR(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDR(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
private static String printLRDBImmUnsignSimplified(int instructionExpression, int offset){
@@ -482,8 +482,8 @@ public class BinToAsmPrinter {
int imm12 = (instructionExpression >> 10 & 0xFFF) * 8; // unsigned only
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDRB(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " LDRB(imm) " + ANSI_GREEN + "%s%d " + ANSI_BLUE + "[%s%d, #0x%x]" + ANSI_RESET + " (note: unsigned offset)\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
wx, Rt, wx, Rn, imm12);
}
@@ -494,9 +494,9 @@ public class BinToAsmPrinter {
int LSL = (instructionExpression >> 22 & 0b1) == 1 ? 12 : 0;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP " + ANSI_GREEN + sf + "%d," +
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP " + ANSI_GREEN + sf + "%d," +
ANSI_BLUE + "0x%x" + ANSI_RESET + " (Real: " + ANSI_BLUE + "#0x%x" + ANSI_RESET + ") " + ANSI_PURPLE + "LSL #%d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
Rn,
conditionalJumpLocation, (conditionalJumpLocation + 0x100),
LSL);
@@ -527,9 +527,9 @@ public class BinToAsmPrinter {
}
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP (sr) " + ANSI_GREEN + sf + "%d," +
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " CMP (sr) " + ANSI_GREEN + sf + "%d," +
ANSI_BLUE + sf + "%d " + ANSI_BLUE + LSLStr + ANSI_PURPLE + " %d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression,
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression,
Rn, Rm, imm6);
}
@@ -544,23 +544,23 @@ public class BinToAsmPrinter {
imm = instructionExpression >> 10 & 0x1fff;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " AND " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " AND " + ANSI_GREEN + sf + "%d, " + ANSI_BLUE +
sf + "%d" + ANSI_PURPLE + " # ??? 0b%s " + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm));
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Rn, Rd, Converter.intToBinaryString(imm));
}
private static String printRetSimplified(int instructionExpression, int offset){
int Xn = (instructionExpression >> 5) & 0x1F;
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " RET " + ANSI_GREEN + " X%d" + ANSI_RESET + "\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression, Xn == 0 ? 30 : Xn);
}
private static String printUnknownSimplified(int instructionExpression, int offset){
return String.format(
- "%05x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
- offset, Integer.reverseBytes(instructionExpression), instructionExpression);
+ "%06x 7100%06x "+ANSI_CYAN+"%08x (%08x)"+ANSI_YELLOW + " ??? 0b"+ANSI_RESET+ Converter.intToBinaryString(instructionExpression) +"\n",
+ offset+0x100, offset,Integer.reverseBytes(instructionExpression), instructionExpression);
}
private static String intAsBinString(int number) {
diff --git a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
index cd2b528..121bd2f 100644
--- a/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
+++ b/src/main/java/nsusbloader/Utilities/patches/fs/FsPatch.java
@@ -148,7 +148,7 @@ public class FsPatch {
private void findAllOffsets() throws Exception{
this.wizard = new HeuristicFsWizard(_textSection);
String errorsAndNotes = wizard.getErrorsAndNotes();
- if (errorsAndNotes.length() > 0)
+ if (! errorsAndNotes.isEmpty())
logPrinter.print(errorsAndNotes, EMsgType.WARNING);
}
private void mkDirs(){
diff --git a/src/main/java/nsusbloader/com/usb/GoldLeaf_111.java b/src/main/java/nsusbloader/com/usb/GoldLeaf_111.java
new file mode 100644
index 0000000..37ae87b
--- /dev/null
+++ b/src/main/java/nsusbloader/com/usb/GoldLeaf_111.java
@@ -0,0 +1,1110 @@
+/*
+ Copyright 2019-2025 Dmitry Isaenko
+
+ This file is part of NS-USBloader.
+
+ NS-USBloader is free software: you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation, either version 3 of the License, or
+ (at your option) any later version.
+
+ NS-USBloader is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with NS-USBloader. If not, see .
+*/
+package nsusbloader.com.usb;
+
+import javafx.application.Platform;
+import javafx.stage.FileChooser;
+import nsusbloader.MediatorControl;
+import nsusbloader.ModelControllers.CancellableRunnable;
+import nsusbloader.ModelControllers.ILogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import nsusbloader.com.helpers.NSSplitReader;
+import org.apache.logging.log4j.core.util.IOUtils;
+import org.usb4java.DeviceHandle;
+import org.usb4java.LibUsb;
+
+import java.io.*;
+import java.nio.ByteBuffer;
+import java.nio.ByteOrder;
+import java.nio.IntBuffer;
+import java.nio.charset.StandardCharsets;
+import java.util.*;
+import java.util.concurrent.CompletableFuture;
+
+/**
+ * GoldLeaf 0.8 processing
+ */
+class GoldLeaf_111 extends TransferModule {
+ private final boolean nspFilterForGl;
+
+ // CMD
+ private final byte[] CMD_GLCO_SUCCESS = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD
+ private final byte[] CMD_GLCO_FAILURE = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xAD, (byte) 0xDE}; // used @ writeToUsb_GLCMD TODO: TEST
+
+ // System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64
+ private final byte[] GL_OBJ_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
+ private final byte[] GL_OBJ_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
+
+ private String recentPath = null;
+ private String[] recentDirs = null;
+ private String[] recentFiles = null;
+
+ private final String[] nspMapKeySetIndexes;
+
+ private String openReadFileNameAndPath;
+ private RandomAccessFile randAccessFile;
+ private NSSplitReader splitReader;
+
+ private final HashMap writeFilesMap = new HashMap<>();
+ private long virtDriveSize;
+ private final HashMap splitFileSize = new HashMap<>();
+
+ private final boolean isWindows = System.getProperty("os.name").contains("Windows");
+ private final String homePath = System.getProperty("user.home") + File.separator;
+ // For using in CMD_SelectFile with SPEC:/ prefix
+ private File selectedFile;
+
+ private final CancellableRunnable task;
+
+ private enum GL_CMD {
+ CMD_GetDriveCount((byte) 1),
+ CMD_GetDriveInfo((byte) 2),
+ CMD_StatPath((byte) 3),
+ CMD_GetFileCount((byte) 4),
+ CMD_GetFile((byte) 5),
+ CMD_GetDirectoryCount((byte) 6),
+ CMD_GetDirectory((byte) 7),
+ CMD_StartFile((byte) 8),
+ CMD_ReadFile((byte) 9),
+ CMD_WriteFile((byte) 10),
+ CMD_EndFile((byte) 11),
+ CMD_Create((byte) 12),
+ CMD_Delete((byte) 13),
+ CMD_Rename((byte) 14),
+ CMD_GetSpecialPathCount((byte) 15),
+ CMD_GetSpecialPath((byte) 16),
+ CMD_SelectFile((byte) 17),
+ CMD_UNKNOWN((byte) 255);
+
+ private final byte id;
+
+ GL_CMD(byte id) {
+ this.id = id;
+ }
+
+ public static GL_CMD get(byte id) {
+ for(GL_CMD cmd : values()) {
+ if(cmd.id == id)
+ return cmd;
+ }
+ return CMD_UNKNOWN;
+ }
+ }
+
+ GoldLeaf_111(DeviceHandle handler,
+ LinkedHashMap nspMap,
+ CancellableRunnable task,
+ ILogPrinter logPrinter,
+ boolean nspFilter) {
+ super(handler, nspMap, task, logPrinter);
+
+ this.task = task;
+ this.nspFilterForGl = nspFilter;
+
+ print("=========== GoldLeaf v1.1.1 ===========\n\t" +
+ "VIRT:/ equals files added into the application\n\t" +
+ "HOME:/ equals "
+ +System.getProperty("user.home"), EMsgType.INFO);
+
+ // Let's collect file names to the array to simplify our life
+ int i = 0;
+ nspMapKeySetIndexes = new String[nspMap.size()];
+ for (String fileName : nspMap.keySet())
+ nspMapKeySetIndexes[i++] = fileName;
+
+ // Calculate size of VIRT:/ drive
+ for (File nspFile : nspMap.values()){
+ if (nspFile.isDirectory()) {
+ var subFiles = nspFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
+ long size = 0;
+ assert subFiles != null;
+ for (File subFile : subFiles) // Validated by parent class
+ size += subFile.length();
+ virtDriveSize += size;
+ splitFileSize.put(nspFile.getName(), size);
+ }
+ else
+ virtDriveSize += nspFile.length();
+ }
+
+ // Go parse commands
+ main_loop:
+ while (true) { // Till user interrupted process.
+ int someLength1, someLength2;
+
+ byte[] readByte = readGL();
+
+ if (readByte == null) // Issue @ readFromUsbGL method
+ return;
+
+ //RainbowHexDump.hexDumpUTF16LE(readByte); // DEBUG
+ //System.out.println("CHOICE: "+readByte[4]); // DEBUG
+ // Arrays.equals(Arrays.copyOfRange(readByte, 0,4), CMD_GLCI)
+
+ if (notGLCI(readByte))
+ continue;
+
+ switch (GL_CMD.get(readByte[4])) {
+ case CMD_GetSpecialPathCount:
+ if (getSpecialPathCount())
+ break main_loop;
+ break;
+ case CMD_GetSpecialPath:
+ if (getSpecialPath(arrToIntLE(readByte,8)))
+ break main_loop;
+ break;
+ case CMD_GetDirectoryCount:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), true))
+ break main_loop;
+ break;
+ case CMD_GetFileCount:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), false))
+ break main_loop;
+ break;
+ case CMD_GetDirectory:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
+ break main_loop;
+ break;
+ case CMD_GetFile:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
+ break main_loop;
+ break;
+ case CMD_StatPath:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
+ break main_loop;
+ break;
+ case CMD_Rename:
+ someLength1 = arrToIntLE(readByte, 12);
+ someLength2 = arrToIntLE(readByte, 16+someLength1);
+ if (rename(new String(readByte, 16, someLength1, StandardCharsets.UTF_8),
+ new String(readByte, 16+someLength1+4, someLength2, StandardCharsets.UTF_8)))
+ break main_loop;
+ break;
+ case CMD_Delete:
+ someLength1 = arrToIntLE(readByte, 12);
+ if (delete(new String(readByte, 16, someLength1, StandardCharsets.UTF_8)))
+ break main_loop;
+ break;
+ case CMD_Create:
+ someLength1 = arrToIntLE(readByte, 12);
+ if (create(new String(readByte, 16, someLength1, StandardCharsets.UTF_8), readByte[8]))
+ break main_loop;
+ break;
+ case CMD_ReadFile:
+ someLength1 = arrToIntLE(readByte, 8);
+ if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8),
+ arrToLongLE(readByte, 12+someLength1),
+ arrToLongLE(readByte, 12+someLength1+8)))
+ break main_loop;
+ break;
+ case CMD_WriteFile:
+ someLength1 = arrToIntLE(readByte, 8);
+ //if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToLongLE(readByte, 12+someLength1)))
+ if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
+ break main_loop;
+ break;
+ case CMD_SelectFile:
+ if (selectFile())
+ break main_loop;
+ break;
+ case CMD_StartFile:
+ case CMD_EndFile:
+ if (startOrEndFile())
+ break main_loop;
+ break;
+ default:
+ writeGL_FAIL("GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
+ }
+ }
+ // Close (and flush) all opened streams.
+ if (! writeFilesMap.isEmpty()){
+ for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
+ try{
+ fBufOutStream.close();
+ } catch (IOException | NullPointerException ignored){}
+ }
+ }
+ closeOpenedReadFilesGl();
+ }
+ private boolean notGLCI(byte[] inputBytes){
+ return ! "GLCI".equals(new String(inputBytes, 0, 4, StandardCharsets.US_ASCII));
+ }
+
+ /**
+ * Close files opened for read/write
+ */
+ private void closeOpenedReadFilesGl(){
+ if (openReadFileNameAndPath != null){ // Perfect time to close our opened files
+ try{
+ randAccessFile.close();
+ }
+ catch (IOException | NullPointerException ignored){}
+ try{
+ splitReader.close();
+ }
+ catch (IOException | NullPointerException ignored){}
+ openReadFileNameAndPath = null;
+ randAccessFile = null;
+ splitReader = null;
+ }
+ }
+ /**
+ * Handle StartFile & EndFile
+ * NOTE: It's something internal for GL and used somehow by GL-PC-app, so just ignore this, at least for v0.8.
+ * @return true - failed, false - passed
+ * */
+ private boolean startOrEndFile(){
+ if (writeGL_PASS()){
+ print("GL Handle 'StartFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Handle GetDriveCount
+ * @return true - failed, false - passed
+ */
+ private boolean getDriveCount(){
+ // Let's declare 2 drives
+ byte[] drivesCnt = intToArrLE(2); //2
+ // Write count of drives
+ if (writeGL_PASS(drivesCnt)) {
+ print("GL Handle 'ListDrives' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Handle GetDriveInfo
+ * @return true - failed, false - passed
+ */
+ private boolean getDriveInfo(int driveNo){
+ if (driveNo < 0 || driveNo > 1){
+ return writeGL_FAIL("GL Handle 'GetDriveInfo' command [no such drive]");
+ }
+
+ byte[] driveLabel,
+ driveLabelLen,
+ driveLetter,
+ driveLetterLen,
+ totalFreeSpace,
+ totalSize;
+ long totalSizeLong;
+
+ // 0 == VIRTUAL DRIVE
+ if (driveNo == 0){
+ driveLabel = "Virtual".getBytes(StandardCharsets.UTF_8);
+ driveLabelLen = intToArrLE(driveLabel.length);
+ driveLetter = "VIRT".getBytes(StandardCharsets.UTF_8); // TODO: Consider moving to class field declaration
+ driveLetterLen = intToArrLE(driveLetter.length);// since GL 0.7
+ totalFreeSpace = new byte[4];
+ totalSizeLong = virtDriveSize;
+ }
+ else { //1 == User home dir
+ driveLabel = "Home".getBytes(StandardCharsets.UTF_8);
+ driveLabelLen = intToArrLE(driveLabel.length);// since GL 0.7
+ driveLetter = "HOME".getBytes(StandardCharsets.UTF_8);
+ driveLetterLen = intToArrLE(driveLetter.length);// since GL 0.7
+ File userHomeDir = new File(System.getProperty("user.home"));
+ long totalFreeSpaceLong = userHomeDir.getFreeSpace();
+ totalFreeSpace = Arrays.copyOfRange(longToArrLE(totalFreeSpaceLong), 0, 4);;
+ totalSizeLong = userHomeDir.getTotalSpace();
+ }
+ totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4);
+
+ List command = new LinkedList<>();
+ command.add(driveLabelLen);
+ command.add(driveLabel);
+ command.add(driveLetterLen);
+ command.add(driveLetter);
+ command.add(totalFreeSpace);
+ command.add(totalSize);
+
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'GetDriveInfo' command", EMsgType.FAIL);
+ return true;
+ }
+
+ return false;
+ }
+ /**
+ * Handle SpecialPathCount
+ * @return true - failed, false - passed
+ * */
+ private boolean getSpecialPathCount(){
+ // Let's declare nothing =)
+ byte[] specialPathCnt = intToArrLE(0);
+ // Write count of special paths
+ if (writeGL_PASS(specialPathCnt)) {
+ print("GL Handle 'SpecialPathCount' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Handle SpecialPath
+ * @return true - failed, false - passed
+ * */
+ private boolean getSpecialPath(int specialPathNo){
+ return writeGL_FAIL("GL Handle 'SpecialPath' command [not supported]");
+ }
+ /**
+ * Handle GetDirectoryCount & GetFileCount
+ * @return true - failed, false - passed
+ * */
+ private boolean getDirectoryOrFileCount(String path, boolean isGetDirectoryCount) {
+ if (path.equals("VIRT:/")) {
+ if (isGetDirectoryCount){
+ if (writeGL_PASS()) {
+ print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else {
+ if (writeGL_PASS(intToArrLE(nspMap.size()))) {
+ print("GL Handle 'GetFileCount' command Count = "+nspMap.size(), EMsgType.FAIL);
+ return true;
+ }
+ }
+ }
+ else if (path.startsWith("HOME:/")){
+ // Let's make it normal path
+ path = updateHomePath(path);
+ // Open it
+ File pathDir = new File(path);
+
+ // Make sure it's exists and it's path
+ if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
+ return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [doesn't exist or not a folder]");
+ // Save recent dir path
+ this.recentPath = path;
+ String[] filesOrDirs;
+ // Now collecting every folder or file inside
+ if (isGetDirectoryCount){
+ filesOrDirs = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (dir.isDirectory() && ! dir.isHidden());
+ });
+ }
+ else {
+ if (nspFilterForGl){
+ filesOrDirs = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (! dir.isDirectory() && name.toLowerCase().endsWith(".nsp"));
+ });
+ }
+ else {
+ filesOrDirs = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (! dir.isDirectory() && (! dir.isHidden()));
+ });
+ }
+ }
+ // If somehow there are no folders, let's say 0;
+ if (filesOrDirs == null){
+ if (writeGL_PASS()) {
+ print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ // Sorting is mandatory NOTE: Proxy tail
+ Arrays.sort(filesOrDirs, String.CASE_INSENSITIVE_ORDER);
+
+ if (isGetDirectoryCount)
+ this.recentDirs = filesOrDirs;
+ else
+ this.recentFiles = filesOrDirs;
+ // Otherwise, let's tell how may folders are in there
+ if (writeGL_PASS(intToArrLE(filesOrDirs.length))) {
+ print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else if (path.startsWith("SPEC:/")){
+ if (isGetDirectoryCount){ // If dir request then 0 dirs
+ if (writeGL_PASS()) {
+ print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else if (selectedFile != null){ // Else it's file request, if we have selected then we will report 1.
+ if (writeGL_PASS(intToArrLE(1))) {
+ print("GL Handle 'GetFileCount' command Count = 1", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+path);
+ }
+ else { // If requested drive is not VIRT and not HOME then reply error
+ return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+path);
+ }
+ return false;
+ }
+ /**
+ * Handle GetDirectory
+ * @return true - failed, false - passed
+ * */
+ private boolean getDirectory(String dirName, int subDirNo){
+ if (dirName.startsWith("HOME:/")) {
+ dirName = updateHomePath(dirName);
+
+ List command = new LinkedList<>();
+
+ if (dirName.equals(recentPath) && recentDirs != null && recentDirs.length != 0){
+ byte[] dirNameBytes = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
+
+ command.add(intToArrLE(dirNameBytes.length));
+ command.add(dirNameBytes);
+ }
+ else {
+ File pathDir = new File(dirName);
+ // Make sure it's exists and it's path
+ if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
+ return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
+ this.recentPath = dirName;
+ // Now collecting every folder or file inside
+ this.recentDirs = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (dir.isDirectory() && ! dir.isHidden()); // TODO: FIX FOR WIN ?
+ });
+ // Check that we still don't have any fuckups
+ if (this.recentDirs != null && this.recentDirs.length > subDirNo){
+ Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
+ byte[] dirBytesName = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(dirBytesName.length));
+ command.add(dirBytesName);
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
+ }
+
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'GetDirectory' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ // VIRT:// and any other
+ return writeGL_FAIL("GL Handle 'GetDirectory' command for virtual drive [no folders support]");
+ }
+ /**
+ * Handle GetFile
+ * @return true - failed, false - passed
+ * */
+ private boolean getFile(String dirName, int subDirNo){
+ List command = new LinkedList<>();
+
+ if (dirName.startsWith("HOME:/")) {
+ dirName = updateHomePath(dirName);
+
+ if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
+ byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
+
+ command.add(intToArrLE(fileNameBytes.length)); //Since GL 0.7
+ command.add(fileNameBytes);
+ }
+ else {
+ File pathDir = new File(dirName);
+ // Make sure it's exists and it's path
+ if ((! pathDir.exists() ) || (! pathDir.isDirectory()) )
+ writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
+ this.recentPath = dirName;
+ // Now collecting every folder or file inside
+ if (nspFilterForGl){
+ this.recentFiles = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (! dir.isDirectory() && name.toLowerCase().endsWith(".nsp")); // TODO: FIX FOR WIN ? MOVE TO PROD
+ });
+ }
+ else {
+ this.recentFiles = pathDir.list((current, name) -> {
+ File dir = new File(current, name);
+ return (! dir.isDirectory() && (! dir.isHidden())); // TODO: FIX FOR WIN
+ });
+ }
+ // Check that we still don't have any fuckups
+ if (this.recentFiles != null && this.recentFiles.length > subDirNo){
+ Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER); // TODO: NOTE: array sorting is an overhead for using poxy loops
+ byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length)); //Since GL 0.7
+ command.add(fileNameBytes);
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
+ }
+
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'GetFile' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ else if (dirName.equals("VIRT:/")){
+ if (! nspMap.isEmpty()){ // therefore nspMapKeySetIndexes also != 0
+ var fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'GetFile' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ else if (dirName.equals("SPEC:/")){
+ if (selectedFile != null){
+ byte[] fileNameBytes = selectedFile.getName().getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(fileNameBytes.length));
+ command.add(fileNameBytes);
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'GetFile' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ // any other cases
+ return writeGL_FAIL("GL Handle 'GetFile' command for virtual drive [no folders support?]");
+ }
+ /**
+ * Handle StatPath
+ * @return true - failed, false - passed
+ * */
+ private boolean statPath(String filePath){
+ var command = new ArrayList();
+
+ if (filePath.startsWith("HOME:/")){
+ filePath = updateHomePath(filePath);
+
+ var fileDirElement = new File(filePath);
+ if (fileDirElement.exists()){
+ if (fileDirElement.isDirectory())
+ command.add(GL_OBJ_TYPE_DIR);
+ else {
+ command.add(GL_OBJ_TYPE_FILE);
+ command.add(longToArrLE(fileDirElement.length()));
+ }
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'StatPath' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ else if (filePath.startsWith("VIRT:/")) {
+ filePath = filePath.replaceFirst("VIRT:/", "");
+ if (nspMap.containsKey(filePath)){
+ command.add(GL_OBJ_TYPE_FILE); // THIS IS INT
+ if (nspMap.get(filePath).isDirectory()) {
+ command.add(longToArrLE(splitFileSize.get(filePath))); // YES, THIS IS LONG!;
+ }
+ else
+ command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
+
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'StatPath' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ else if (filePath.startsWith("SPEC:/")){
+ //System.out.println(filePath);
+ filePath = filePath.replaceFirst("SPEC:/","");
+ if (selectedFile.getName().equals(filePath)){
+ command.add(GL_OBJ_TYPE_FILE);
+ command.add(longToArrLE(selectedFile.length()));
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'StatPath' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ return writeGL_FAIL("GL Handle 'StatPath' command [no such folder] - "+filePath);
+ }
+ /**
+ * Handle 'Rename' that is actually 'mv'
+ * @return true - failed, false - passed
+ * */
+ private boolean rename(String fileName, String newFileName){
+ if (fileName.startsWith("HOME:/")){
+ // This shit takes too much time to explain, but such behaviour won't let GL to fail
+ this.recentPath = null;
+ this.recentFiles = null;
+ this.recentDirs = null;
+ fileName = updateHomePath(fileName);
+ newFileName = updateHomePath(newFileName);
+
+ File currentFile = new File(fileName);
+ File newFile = new File(newFileName);
+ if (! newFile.exists()){ // Else, report error
+ try {
+ if (currentFile.renameTo(newFile)){
+ if (writeGL_PASS()) {
+ print("GL Handle 'Rename' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (SecurityException ignored){} // Ah, leave it
+ }
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Rename' command [not supported for virtual drive/wrong drive/file with such name already exists/read-only directory]");
+ }
+ /**
+ * Handle 'Delete'
+ * @return true - failed, false - passed
+ * */
+ private boolean delete(String fileName) {
+ if (fileName.startsWith("HOME:/")) {
+ fileName = updateHomePath(fileName);
+
+ File fileToDel = new File(fileName);
+ try {
+ if (fileToDel.delete()){
+ if (writeGL_PASS()) {
+ print("GL Handle 'Rename' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (SecurityException ignored){} // Ah, leave it
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
+ }
+ /**
+ * Handle 'Create'
+ * @param type 1 for file
+ * 2 for folder
+ * @param fileName full path including new file name in the end
+ * @return true - failed, false - passed
+ * */
+ private boolean create(String fileName, byte type) {
+ if (fileName.startsWith("HOME:/")) {
+ fileName = updateHomePath(fileName);
+ File fileToCreate = new File(fileName);
+ boolean result = false;
+ if (type == 1){
+ try {
+ result = fileToCreate.createNewFile();
+ }
+ catch (SecurityException | IOException ignored){}
+ }
+ else if (type == 2){
+ try {
+ result = fileToCreate.mkdir();
+ }
+ catch (SecurityException ignored){}
+ }
+ if (result){
+ if (writeGL_PASS()) {
+ print("GL Handle 'Create' command.", EMsgType.FAIL);
+ return true;
+ }
+ //print("GL Handle 'Create' command.", EMsgType.PASS);
+ return false;
+ }
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
+ }
+
+ /**
+ * Handle 'ReadFile'
+ * @param fileName full path including new file name in the end
+ * @param offset requested offset
+ * @param size requested size
+ * @return true - failed, false - passed
+ * */
+ private boolean readFile(String fileName, long offset, long size) {
+ //System.out.println("readFile "+fileName+"\t"+offset+"\t"+size+"\n");
+ if (fileName.startsWith("VIRT:/")){
+ // Let's find out which file requested
+ String fNamePath = nspMap.get(fileName.substring(6)).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
+ // If we don't have this file opened, let's open it
+ if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fNamePath))) {
+ // Try close what opened
+ if (openReadFileNameAndPath != null){
+ try{
+ randAccessFile.close();
+ }catch (Exception ignored){}
+ try{
+ splitReader.close();
+ }catch (Exception ignored){}
+ }
+ // Open what has to be opened
+ try{
+ File tempFile = nspMap.get(fileName.substring(6));
+ if (tempFile.isDirectory()) {
+ randAccessFile = null;
+ splitReader = new NSSplitReader(tempFile, 0);
+ }
+ else {
+ splitReader = null;
+ randAccessFile = new RandomAccessFile(tempFile, "r");
+ }
+ openReadFileNameAndPath = fNamePath;
+ }
+ catch (IOException | NullPointerException ioe){
+ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ }
+ else {
+ // Let's find out which file requested
+ fileName = updateHomePath(fileName);
+ // If we don't have this file opened, let's open it
+ if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fileName))) {
+ // Try close what opened
+ if (openReadFileNameAndPath != null){
+ try{
+ randAccessFile.close();
+ }catch (IOException | NullPointerException ignored){}
+ }
+ // Open what has to be opened
+ try{
+ randAccessFile = new RandomAccessFile(fileName, "r");
+ openReadFileNameAndPath = fileName;
+ }catch (IOException | NullPointerException ioe){
+ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ }
+ //----------------------- Actual transfer chain ------------------------
+ try{
+ if (randAccessFile == null){
+ splitReader.seek(offset);
+ byte[] chunk = new byte[(int)size]; // WTF MAN?
+ // Let's find out how much bytes we got
+ int bytesRead = splitReader.read(chunk);
+ // Let's check that we read expected size
+ if (bytesRead != (int)size)
+ return writeGL_FAIL("GL Handle 'ReadFile' command [CMD]" +
+ "\n At offset: " + offset +
+ "\n Requested: " + size +
+ "\n Received: " + bytesRead);
+ // Let's tell as a command about our result.
+ if (writeGL_PASS(longToArrLE(size))) {
+ print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
+ return true;
+ }
+ // Let's bypass bytes we read total
+ if (writeToUsb(chunk)) {
+ print("GL Handle 'ReadFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ else {
+ randAccessFile.seek(offset);
+ byte[] chunk = new byte[(int)size]; // yes, I know, but nothing to do here.
+ // Let's find out how many bytes we got
+ int bytesRead = randAccessFile.read(chunk);
+ // Let's check that we read expected size
+ if (bytesRead != (int) size)
+ return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
+ // Let's tell as a command about our result.
+ if (writeGL_PASS(longToArrLE(size))) {
+ print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
+ return true;
+ }
+ // Let's bypass bytes we read total
+ if (writeToUsb(chunk)) {
+ print("GL Handle 'ReadFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (Exception ioe){
+ try{
+ randAccessFile.close();
+ }
+ catch (NullPointerException ignored){}
+ catch (IOException ioe_){
+ print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
+ }
+ try{
+ splitReader.close();
+ }
+ catch (NullPointerException ignored){}
+ catch (IOException ioe_){
+ print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
+ }
+ openReadFileNameAndPath = null;
+ randAccessFile = null;
+ splitReader = null;
+ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ /**
+ * Handle 'WriteFile'
+ * @param fileName full path including new file name in the end
+ *
+ * @return true - failed, false - passed
+ * */
+ //@param size requested size
+ //private boolean writeFile(String fileName, long size) {
+ private boolean writeFile(String fileName) {
+ if (fileName.startsWith("VIRT:/")){
+ return writeGL_FAIL("GL Handle 'WriteFile' command [not supported for virtual drive]");
+ }
+
+ fileName = updateHomePath(fileName);
+ // Check if we didn't see this (or any) file during this session
+ if (writeFilesMap.isEmpty() || (! writeFilesMap.containsKey(fileName))){
+ // Open what we have to open
+ File writeFile = new File(fileName);
+ // If this file exists GL will take care
+ // Otherwise, let's add it
+ try{
+ BufferedOutputStream writeFileBufOutStream = new BufferedOutputStream(new FileOutputStream(writeFile, true));
+ writeFilesMap.put(fileName, writeFileBufOutStream);
+ } catch (IOException ioe){
+ return writeGL_FAIL("GL Handle 'WriteFile' command [IOException]\n\t"+ioe.getMessage());
+ }
+ }
+ // Now we have stream
+ BufferedOutputStream myStream = writeFilesMap.get(fileName);
+
+ byte[] transferredData;
+
+ if ((transferredData = readGL_file()) == null){
+ print("GL Handle 'WriteFile' command [1/1]", EMsgType.FAIL);
+ return true;
+ }
+ try{
+ myStream.write(transferredData, 0, transferredData.length);
+ }
+ catch (IOException ioe){
+ return writeGL_FAIL("GL Handle 'WriteFile' command [1/1]\n\t"+ioe.getMessage());
+ }
+ // Report we're good
+ if (writeGL_PASS()) {
+ print("GL Handle 'WriteFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Handle 'SelectFile'
+ * @return true - failed, false - passed
+ * */
+ private boolean selectFile(){
+ File selectedFile = CompletableFuture.supplyAsync(() -> {
+ FileChooser fChooser = new FileChooser();
+ fChooser.setTitle(MediatorControl.INSTANCE.getResourceBundle().getString("btn_OpenFile")); // TODO: FIX BAD IMPLEMENTATION
+ fChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: Consider fixing; not a priority.
+ fChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("*", "*"));
+ return fChooser.showOpenDialog(null); // Leave as is for now.
+ }, Platform::runLater).join();
+
+ if (selectedFile == null){ // Nothing selected
+ this.selectedFile = null;
+ return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
+ }
+
+ List command = new LinkedList<>();
+ byte[] selectedFileNameBytes = ("SPEC:/"+selectedFile.getName()).getBytes(StandardCharsets.UTF_8);
+ command.add(intToArrLE(selectedFileNameBytes.length));
+ command.add(selectedFileNameBytes);
+ if (writeGL_PASS(command)) {
+ print("GL Handle 'SelectFile' command", EMsgType.FAIL);
+ this.selectedFile = null;
+ return true;
+ }
+ this.selectedFile = selectedFile;
+ return false;
+ }
+
+ /*----------------------------------------------------*/
+ /* GL HELPERS */
+ /*----------------------------------------------------*/
+ /**
+ * Convert path received from GL to normal
+ */
+ private String updateHomePath(String glPath){
+ String updatedGlPath;
+ if (isWindows)
+ updatedGlPath = glPath.replaceAll("/", "\\\\");
+ updatedGlPath = homePath + glPath.substring(6); // Do not use replaceAll since it will consider \ as special directive
+ return updatedGlPath;
+ }
+ /**
+ * Convert INT (Little endian) value to bytes-array representation
+ * */
+ private 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
+ * */
+ private byte[] longToArrLE(long value){
+ return ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN)
+ .putLong(value)
+ .array();
+ }
+ /**
+ * Convert bytes-array to INT value (Little endian)
+ * */
+ private int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
+ }
+ /**
+ * Convert bytes-array to LONG value (Little endian)
+ * */
+ private long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+ /*----------------------------------------------------*/
+ /* GL READ/WRITE USB SPECIFIC */
+ /*----------------------------------------------------*/
+
+ private byte[] readGL(){
+ var readBuffer = ByteBuffer.allocateDirect(4096);
+ var readBufTransferred = IntBuffer.allocate(1);
+
+ while (! task.isCancelled()) {
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ 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: " +
+ UsbErrorCodes.getErrCode(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, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ byte[] receivedBytes = new byte[readBufTransferred.get()];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ print("GL Data transfer issue [read]\n Returned: " +
+ UsbErrorCodes.getErrCode(result) +
+ "\n GL Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ print("GL Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+ /**
+ * Write new command. Shitty implementation.
+ * */
+ private boolean writeGL_PASS(byte[] message){
+ return writeToUsb(
+ ByteBuffer.allocate(4096)
+ .put(CMD_GLCO_SUCCESS)
+ .put(message)
+ .array());
+ }
+ private boolean writeGL_PASS(){
+ return writeToUsb(Arrays.copyOf(CMD_GLCO_SUCCESS, 4096));
+ }
+ private boolean writeGL_PASS(List messages){
+ var writeBuffer = ByteBuffer.allocate(4096)
+ .put(CMD_GLCO_SUCCESS);
+ messages.forEach(writeBuffer::put);
+ return writeToUsb(writeBuffer.array());
+ }
+
+ private boolean writeGL_FAIL(String reportToUImsg){
+ if (writeToUsb(Arrays.copyOf(CMD_GLCO_FAILURE, 4096))){
+ print(reportToUImsg, EMsgType.WARNING);
+ return true;
+ }
+ print(reportToUImsg, EMsgType.FAIL);
+ return false;
+ }
+ /**
+ * Sending any byte array to USB device
+ * @return 'false' if no issues
+ * 'true' if errors happened
+ * */
+ private boolean writeToUsb(byte[] message){
+ //System.out.println(">");
+ //RainbowHexDump.hexDumpUTF16LE(message); // DEBUG
+ var writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
+ writeBuffer.put(message); // Don't do writeBuffer.rewind();
+ var writeBufTransferred = IntBuffer.allocate(1);
+
+ while (! task.isCancelled()) {
+ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
+
+ switch (result){
+ case LibUsb.SUCCESS:
+ if (writeBufTransferred.get() == message.length)
+ return false;
+ else {
+ print("GL Data transfer issue [write]\n Requested: " +
+ message.length +
+ "\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
+ return true;
+ }
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ print("GL Data transfer issue [write]\n Returned: " +
+ UsbErrorCodes.getErrCode(result) +
+ "\n GL Execution stopped", EMsgType.FAIL);
+ return true;
+ }
+ }
+ print("GL Execution interrupted", EMsgType.INFO);
+ return true;
+ }
+}
diff --git a/src/main/java/nsusbloader/com/usb/UsbCommunications.java b/src/main/java/nsusbloader/com/usb/UsbCommunications.java
index 76063d4..e426d0c 100644
--- a/src/main/java/nsusbloader/com/usb/UsbCommunications.java
+++ b/src/main/java/nsusbloader/com/usb/UsbCommunications.java
@@ -1,5 +1,5 @@
/*
- Copyright 2019-2020 Dmitry Isaenko
+ Copyright 2019-2025 Dmitry Isaenko
This file is part of NS-USBloader.
@@ -66,7 +66,10 @@ public class UsbCommunications extends CancellableRunnable {
case "TinFoil":
module = new TinFoil(handler, nspMap, this, logPrinter);
break;
- case "GoldLeafv0.10+":
+ case "GoldLeafv1.1.1+":
+ module = new GoldLeaf_111(handler, nspMap, this, logPrinter, nspFilterForGl);
+ break;
+ case "GoldLeafv0.10-1.1.0":
module = new GoldLeaf_010(handler, nspMap, this, logPrinter, nspFilterForGl);
break;
case "GoldLeafv0.8-0.9":
diff --git a/src/test/java/integration/EsIntegrationTest.java b/src/test/java/integration/EsIntegrationTest.java
index 8fb868c..dc13b06 100644
--- a/src/test/java/integration/EsIntegrationTest.java
+++ b/src/test/java/integration/EsIntegrationTest.java
@@ -8,7 +8,7 @@ import org.junit.jupiter.api.*;
import java.io.File;
import java.util.Arrays;
-@Disabled
+//@Disabled
public class EsIntegrationTest {
static String pathToFirmware;
static String pathToFirmwares;
@@ -22,18 +22,17 @@ public class EsIntegrationTest {
pathToKeysFile = environment.getProdkeysLocation();
saveTo = environment.getSaveToLocation() + File.separator + "ES_LPR";
pathToFirmwares = environment.getFirmwaresLocation();
- pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
+ pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 19.0.1";
}
@DisplayName("ES Integration validation - everything")
@Test
void makeEss() throws Exception{
- File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
- return s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*");
- //return s.matches("^Firmware 10.0.1.*");
- });
+ File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
+ s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*"));
assert fwDirs != null;
Arrays.sort(fwDirs);
+ Arrays.stream(fwDirs).forEach(System.out::println);
for (File dir : fwDirs){
EsPatchMaker esPatchMaker = new EsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
Thread workThread = new Thread(esPatchMaker);
@@ -42,6 +41,7 @@ public class EsIntegrationTest {
}
}
+ @Disabled
@DisplayName("ES Integration validation - one particular firmware")
@Test
void makeEs() throws Exception{
diff --git a/src/test/java/integration/FsIntegrationTest.java b/src/test/java/integration/FsIntegrationTest.java
index f56544c..a68530d 100644
--- a/src/test/java/integration/FsIntegrationTest.java
+++ b/src/test/java/integration/FsIntegrationTest.java
@@ -11,7 +11,7 @@ import org.junit.jupiter.api.Test;
import java.io.File;
import java.util.Arrays;
-@Disabled
+//@Disabled
public class FsIntegrationTest {
static String pathToFirmware;
static String pathToFirmwares;
@@ -25,19 +25,17 @@ public class FsIntegrationTest {
pathToKeysFile = environment.getProdkeysLocation();
saveTo = environment.getSaveToLocation() + File.separator + "FS_LPR";
pathToFirmwares = environment.getFirmwaresLocation();
- pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 17.0.0";
+ pathToFirmware = environment.getFirmwaresLocation() + File.separator + "Firmware 15.0.0";
}
@DisplayName("FS Integration validation - everything")
@Test
void makeFss() throws Exception{
- File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) -> {
- return (s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
- //return s.matches("^Firmware 10.0.1.*");
- });
+ File[] fwDirs = new File(pathToFirmwares).listFiles((file, s) ->
+ s.matches("^Firmware (9\\.|[0-9][0-9]\\.).*") && ! s.endsWith(".zip"));
assert fwDirs != null;
Arrays.sort(fwDirs);
-
+ Arrays.stream(fwDirs).forEach(System.out::println);
for (File dir : fwDirs){
System.out.println("\n\t\t\t"+dir.getName());
FsPatchMaker fsPatchMaker = new FsPatchMaker(dir.getAbsolutePath(), pathToKeysFile, saveTo);
@@ -47,6 +45,7 @@ public class FsIntegrationTest {
}
}
+ @Disabled
@DisplayName("FS Integration validation - one particular firmware")
@Test
void makeFs() throws Exception{
diff --git a/src/test/java/integration/LoaderIntegrationTest.java b/src/test/java/integration/LoaderIntegrationTest.java
index 25a448a..aa9956a 100644
--- a/src/test/java/integration/LoaderIntegrationTest.java
+++ b/src/test/java/integration/LoaderIntegrationTest.java
@@ -10,7 +10,7 @@ import org.junit.jupiter.api.Test;
import java.io.File;
-@Disabled
+//@Disabled
public class LoaderIntegrationTest {
static String pathToAtmo;
static String saveTo;