continue refactoring of GL
Some checks failed
ci/woodpecker/push/woodpecker Pipeline failed

This commit is contained in:
Dmitry Isaenko 2025-08-31 01:47:31 +03:00
parent 02a2200d04
commit 0a9883ad2b
6 changed files with 271 additions and 254 deletions

View file

@ -50,7 +50,7 @@
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format> <maven.build.timestamp.format>yyyyMMdd.HHmmss</maven.build.timestamp.format>
<javafx.version>19.0.2.1</javafx.version> <javafx.version>19.0.2.1</javafx.version>
<maven.compiler.release>11</maven.compiler.release> <maven.compiler.release>17</maven.compiler.release>
</properties> </properties>
<issueManagement> <issueManagement>
@ -205,7 +205,7 @@
<artifactId>maven-compiler-plugin</artifactId> <artifactId>maven-compiler-plugin</artifactId>
<version>3.10.1</version> <version>3.10.1</version>
<configuration> <configuration>
<release>11</release> <release>17</release>
</configuration> </configuration>
</plugin> </plugin>
<!-- Don't generate default JAR without dependencies --> <!-- Don't generate default JAR without dependencies -->
@ -274,7 +274,7 @@
<!-- <dontWrapJar>true</dontWrapJar> --> <!-- <dontWrapJar>true</dontWrapJar> -->
<jre> <jre>
<path>%PWD%/jdk</path> <path>%PWD%/jdk</path>
<minVersion>11.0.0</minVersion> <minVersion>17.0.0</minVersion>
</jre> </jre>
<versionInfo> <versionInfo>
<fileVersion>${project.version}.0.0</fileVersion> <fileVersion>${project.version}.0.0</fileVersion>

View file

@ -31,14 +31,17 @@ public abstract class TransferModule {
protected static final byte IN_EP = (byte) 0x81; protected static final byte IN_EP = (byte) 0x81;
protected static final byte OUT_EP = (byte) 0x01; protected static final byte OUT_EP = (byte) 0x01;
EFileStatus status = EFileStatus.UNKNOWN; protected EFileStatus status = EFileStatus.UNKNOWN;
LinkedHashMap<String, File> nspMap; protected LinkedHashMap<String, File> nspMap;
ILogPrinter logPrinter; protected ILogPrinter logPrinter;
DeviceHandle handlerNS; protected DeviceHandle handlerNS;
CancellableRunnable task; protected CancellableRunnable task;
TransferModule(DeviceHandle handler, LinkedHashMap<String, File> nspMap, CancellableRunnable task, ILogPrinter printer){ protected TransferModule(DeviceHandle handler,
LinkedHashMap<String, File> nspMap,
CancellableRunnable task,
ILogPrinter printer){
this.handlerNS = handler; this.handlerNS = handler;
this.nspMap = nspMap; this.nspMap = nspMap;
this.task = task; this.task = task;
@ -81,7 +84,7 @@ public abstract class TransferModule {
} }
public EFileStatus getStatus(){ return status; } public EFileStatus getStatus(){ return status; }
void print(String message, EMsgType type){ protected void print(String message, EMsgType type){
try { try {
logPrinter.print(message, type); logPrinter.print(message, type);
} }

View file

@ -24,6 +24,7 @@ import nsusbloader.ModelControllers.Log;
import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.NSLDataTypes.EModule; import nsusbloader.NSLDataTypes.EModule;
import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.com.usb.gl.GoldLeaf_111;
import org.usb4java.*; import org.usb4java.*;
import java.io.*; import java.io.*;

View file

@ -0,0 +1,53 @@
/*
Copyright 2019-2025 Dmitry Isaenko
This file is part of NS-USBloader.
NS-USBloader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
NS-USBloader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.com.usb.gl;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
public class Converters {
/**
* Convert INT (Little endian) value to bytes-array representation
* */
public static byte[] intToArrLE(int value){
return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN)
.putInt(value)
.array();
}
/**
* Convert LONG (Little endian) value to bytes-array representation
* */
public static byte[] longToArrLE(long value){
return ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN)
.putLong(value)
.array();
}
/**
* Convert bytes-array to INT value (Little endian)
* */
public static int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
}
/**
* Convert bytes-array to LONG value (Little endian)
* */
public static long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
}
}

View file

@ -16,31 +16,33 @@
You should have received a copy of the GNU General Public License You should have received a copy of the GNU General Public License
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>. along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/ */
package nsusbloader.com.usb; package nsusbloader.com.usb.gl;
import javafx.application.Platform; import javafx.application.Platform;
import javafx.stage.FileChooser; import javafx.stage.FileChooser;
import libKonogonka.RainbowDump;
import nsusbloader.MediatorControl; import nsusbloader.MediatorControl;
import nsusbloader.ModelControllers.CancellableRunnable; import nsusbloader.ModelControllers.CancellableRunnable;
import nsusbloader.ModelControllers.ILogPrinter; import nsusbloader.ModelControllers.ILogPrinter;
import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.com.helpers.NSSplitReader; import nsusbloader.com.helpers.NSSplitReader;
import nsusbloader.com.usb.TransferModule;
import nsusbloader.com.usb.UsbErrorCodes;
import org.usb4java.DeviceHandle; import org.usb4java.DeviceHandle;
import org.usb4java.LibUsb; import org.usb4java.LibUsb;
import java.io.*; import java.io.*;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.IntBuffer; import java.nio.IntBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.CompletableFuture; import java.util.concurrent.CompletableFuture;
import static nsusbloader.com.usb.gl.Converters.*;
/** /**
* GoldLeaf 1.1.1 processing * GoldLeaf 1.1.1 processing
*/ */
class GoldLeaf_111 extends TransferModule { public class GoldLeaf_111 extends TransferModule {
// CMD // CMD
private final static byte[] CMD_GLCO_FAILURE = private final static byte[] CMD_GLCO_FAILURE =
Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xAD, (byte) 0xDE}, 4096); // used @ writeToUsb_GLCMD Arrays.copyOf(new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, (byte) 0xAD, (byte) 0xDE}, 4096); // used @ writeToUsb_GLCMD
@ -48,8 +50,8 @@ class GoldLeaf_111 extends TransferModule {
new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD
// System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64 // 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_OBJECT_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
private final byte[] GL_OBJ_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00}; private final byte[] GL_OBJECT_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
private final boolean nspFilter; private final boolean nspFilter;
@ -72,59 +74,20 @@ class GoldLeaf_111 extends TransferModule {
// For using in CMD_SelectFile with SPEC:/ prefix // For using in CMD_SelectFile with SPEC:/ prefix
private File selectedFile; private File selectedFile;
private final CancellableRunnable task; public GoldLeaf_111(DeviceHandle handler,
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<String, File> nspMap, LinkedHashMap<String, File> nspMap,
CancellableRunnable task, CancellableRunnable task,
ILogPrinter logPrinter, ILogPrinter logPrinter,
boolean nspFilter) { boolean nspFilter) {
super(handler, nspMap, task, logPrinter); super(handler, nspMap, task, logPrinter);
this.task = task;
this.nspFilter = nspFilter; this.nspFilter = nspFilter;
print("=========== GoldLeaf v1.1.1 ===========\n\t" + print("=========== GoldLeaf v1.1.1 ===========\n\t" +
"VIRT:/ equals files added into the application\n\t" + "VIRT:/ equals files added into the application\n\t" +
"HOME:/ equals " "HOME:/ equals " +homePath, EMsgType.INFO);
+System.getProperty("user.home"), EMsgType.INFO);
// Let's collect file names to the array to simplify our life // Let's collect file names to the array (simplifies flow)
nspMapKeySetIndexes = nspMap.keySet().toArray(new String[0]); nspMapKeySetIndexes = nspMap.keySet().toArray(new String[0]);
// Calculate size of VIRT:/ drive // Calculate size of VIRT:/ drive
@ -152,90 +115,88 @@ class GoldLeaf_111 extends TransferModule {
return; return;
//RainbowDump.hexDumpUTF8(readByte); // DEBUG //RainbowDump.hexDumpUTF8(readByte); // DEBUG
System.out.println("\t→: "+GL_CMD.get(readByte[4])); // FIXME: DEBUG //System.out.println("\t→ "+ GoldleafCmd.get(readByte[4]));
if (notGLCI(readByte)) if (notGLCI(readByte))
continue; continue;
switch (GL_CMD.get(readByte[4])) { switch (GoldleafCmd.get(readByte[4])) {
case CMD_GetDriveCount: case GetDriveCount:
if (getDriveCount()) if (getDriveCount())
break main_loop; break main_loop;
break; break;
case CMD_GetDriveInfo: case GetDriveInfo:
if (getDriveInfo(arrToIntLE(readByte,8))) if (getDriveInfo(arrToIntLE(readByte,8)))
break main_loop; break main_loop;
break; break;
case CMD_GetSpecialPathCount: case GetSpecialPathCount:
if (getSpecialPathCount()) if (getSpecialPathCount())
break main_loop; break main_loop;
break; break;
case CMD_GetSpecialPath: case GetSpecialPath:
if (getSpecialPath(arrToIntLE(readByte,8))) if (getSpecialPath(arrToIntLE(readByte,8)))
break main_loop; break main_loop;
break; break;
case CMD_GetDirectoryCount: case GetDirectoryCount:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), true)) if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), true))
break main_loop; break main_loop;
break; break;
case CMD_GetFileCount: case GetFileCount:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), false)) if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), false))
break main_loop; break main_loop;
break; break;
case CMD_GetDirectory: case GetDirectory:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12))) if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
break main_loop; break main_loop;
break; break;
case CMD_GetFile: case GetFile:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12))) if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), arrToIntLE(readByte, someLength1+12)))
break main_loop; break main_loop;
break; break;
case CMD_StatPath: case StatPath:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_8))) if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
break main_loop; break main_loop;
break; break;
case CMD_Rename: case Rename:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
someLength2 = arrToIntLE(readByte, 12+someLength1); someLength2 = arrToIntLE(readByte, 12+someLength1);
if (rename(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), // 8+4=12 if (rename(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), // 8+4=12
new String(readByte, 12+someLength1+4, someLength2, StandardCharsets.UTF_8))) new String(readByte, 12+someLength1+4, someLength2, StandardCharsets.UTF_8)))
break main_loop; break main_loop;
break; break;
case CMD_Delete: case Delete:
RainbowDump.hexDumpUTF8(readByte); // TODO: DEBUG someLength1 = arrToIntLE(readByte, 8);
someLength1 = arrToIntLE(readByte, 12); // FIXME TEST ME! if (delete(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
if (delete(new String(readByte, 16, someLength1, StandardCharsets.UTF_8)))
break main_loop; break main_loop;
break; break;
case CMD_Create: case Create:
someLength1 = arrToIntLE(readByte, 12); // FIXME TEST ME! someLength1 = arrToIntLE(readByte, 8);
if (create(new String(readByte, 16, someLength1, StandardCharsets.UTF_8), readByte[8])) if (create(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), readByte[8]))
break main_loop; break main_loop;
break; break;
case CMD_ReadFile: case ReadFile:
someLength1 = arrToIntLE(readByte, 8); someLength1 = arrToIntLE(readByte, 8);
if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8), if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8),
arrToLongLE(readByte, 12+someLength1), arrToLongLE(readByte, 12+someLength1),
arrToLongLE(readByte, 12+someLength1+8))) arrToLongLE(readByte, 12+someLength1+8)))
break main_loop; break main_loop;
break; break;
case CMD_WriteFile: case WriteFile:
someLength1 = arrToIntLE(readByte, 8); 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))) if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_8)))
break main_loop; break main_loop;
break; break;
case CMD_SelectFile: case SelectFile:
if (selectFile()) if (selectFile())
break main_loop; break main_loop;
break; break;
case CMD_StartFile: case StartFile:
case CMD_EndFile: case EndFile:
if (startOrEndFile()) if (startOrEndFile())
break main_loop; break main_loop;
break; break;
@ -256,37 +217,6 @@ class GoldLeaf_111 extends TransferModule {
return ! "GLCI".equals(new String(inputBytes, 0, 4, StandardCharsets.US_ASCII)); return ! "GLCI".equals(new String(inputBytes, 0, 4, StandardCharsets.US_ASCII));
} }
/**
* Close files opened for read/write
*/
private void closeOpenedReadFilesGl(){
if (openReadFileNameAndPath != null){
closeRAFandSplitReader();
openReadFileNameAndPath = null;
randAccessFile = null;
splitReader = null;
}
}
private void closeRAFandSplitReader(){
closeRAF();
try{
splitReader.close();
}
catch (IOException ioe_){
print("Unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
}
catch (Exception ignored){}
}
private void closeRAF(){
try{
randAccessFile.close();
}
catch (IOException ioe_){
print("Unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
}
catch (Exception ignored){}
}
/** /**
* Handle StartFile & EndFile * 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. * NOTE: It's something internal for GL and used somehow by GL-PC-app, so just ignore this, at least for v0.8.
@ -315,8 +245,7 @@ class GoldLeaf_111 extends TransferModule {
driveLabelLen, driveLabelLen,
driveLetter, driveLetter,
driveLetterLen, driveLetterLen,
totalFreeSpace, totalFreeSpace;
totalSize;
long totalSizeLong; long totalSizeLong;
if (driveNo == 0){ // 0 == VIRTUAL DRIVE if (driveNo == 0){ // 0 == VIRTUAL DRIVE
@ -336,7 +265,7 @@ class GoldLeaf_111 extends TransferModule {
totalFreeSpace = Arrays.copyOfRange(longToArrLE(userHomeDir.getFreeSpace()), 0, 4);; totalFreeSpace = Arrays.copyOfRange(longToArrLE(userHomeDir.getFreeSpace()), 0, 4);;
totalSizeLong = userHomeDir.getTotalSpace(); totalSizeLong = userHomeDir.getTotalSpace();
} }
totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); var totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4);
var command = Arrays.asList( var command = Arrays.asList(
driveLabelLen, driveLabelLen,
@ -367,24 +296,25 @@ class GoldLeaf_111 extends TransferModule {
* Handle GetDirectoryCount & GetFileCount * Handle GetDirectoryCount & GetFileCount
* @return true - failed, false - passed * @return true - failed, false - passed
* */ * */
private boolean getDirectoryOrFileCount(String path, boolean isGetDirectoryCount) { private boolean getDirectoryOrFileCount(String glFileName, boolean isGetDirectoryCount) {
if (path.equals("VIRT:/")) { if (glFileName.equals("VIRT:/")) {
return isGetDirectoryCount ? return isGetDirectoryCount ?
writeGL_PASS("GL Handle 'GetDirectoryCount' command") : writeGL_PASS("GL Handle 'GetDirectoryCount' command") :
writeGL_PASS(intToArrLE(nspMap.size()), "GL Handle 'GetFileCount' command Count = " + nspMap.size()); writeGL_PASS(intToArrLE(nspMap.size()), "GL Handle 'GetFileCount' command Count = " + nspMap.size());
} }
else if (path.startsWith("HOME:/")){ else if (glFileName.startsWith("HOME:/")){
// Let's make it normal path var path = updateHomePath(glFileName);
path = updateHomePath(path);
var pathDir = new File(path); var pathDir = new File(path);
// Make sure it's exists and it's path if (notExistsOrDirectory(pathDir))
if ((! pathDir.exists() ) || (! pathDir.isDirectory()) ) return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [doesn't exist or not a folder] "+ pathDir);
return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [doesn't exist or not a folder]");
// Save recent dir path this.recentPath = path; // Save recent dir path
this.recentPath = path; var filesOrDirs = isGetDirectoryCount ?
var filesOrDirs = getFilesOrDirs(isGetDirectoryCount, pathDir); pathDir.list(this::isDirectoryAndNotHidden) :
// If somehow there are no folders, let's say 0; pathDir.list(this::isFileAndNotHidden);
// If no folders, let's say 0;
if (filesOrDirs == null) if (filesOrDirs == null)
return writeGL_PASS("GL Handle 'GetDirectoryOrFileCount' command"); return writeGL_PASS("GL Handle 'GetDirectoryOrFileCount' command");
// Sorting is mandatory NOTE: Proxy tail // Sorting is mandatory NOTE: Proxy tail
@ -397,34 +327,15 @@ class GoldLeaf_111 extends TransferModule {
// Otherwise, let's tell how may folders are in there // Otherwise, let's tell how may folders are in there
return writeGL_PASS(intToArrLE(filesOrDirs.length), "GL Handle 'GetDirectoryOrFileCount' command"); return writeGL_PASS(intToArrLE(filesOrDirs.length), "GL Handle 'GetDirectoryOrFileCount' command");
} }
else if (path.startsWith("SPEC:/")){ else if (glFileName.startsWith("SPEC:/")){
if (isGetDirectoryCount) // If dir request then 0 dirs if (isGetDirectoryCount) // If dir request then 0 dirs
return writeGL_PASS("GL Handle 'GetDirectoryCount' command"); 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. 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_PASS(intToArrLE(1), "GL Handle 'GetFileCount' command Count = 1");
return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+path); return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+glFileName);
} }
// If requested drive is not VIRT and not HOME then reply error // 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 writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+glFileName);
}
private String[] getFilesOrDirs(boolean isGetDirectoryCount, File pathDir) {
String[] filesOrDirs;
// Now collecting every folder or file inside
if (isGetDirectoryCount){
filesOrDirs = pathDir.list((current, name) -> {
var dir = new File(current, name);
return (dir.isDirectory() && ! dir.isHidden());
});
}
else {
filesOrDirs = pathDir.list((current, name) -> {
var dir = new File(current, name);
return (! dir.isDirectory() && nspFilter ?
name.toLowerCase().endsWith(".nsp") :
! dir.isHidden());
});
}
return filesOrDirs;
} }
/** /**
@ -445,14 +356,11 @@ class GoldLeaf_111 extends TransferModule {
else { else {
var pathDir = new File(dirName); var pathDir = new File(dirName);
// Make sure it's exists and it's path // Make sure it's exists and it's path
if ((! pathDir.exists() ) || (! pathDir.isDirectory()) ) if (notExistsOrDirectory(pathDir))
return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]"); return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
this.recentPath = dirName; this.recentPath = dirName;
// Now collecting every folder or file inside // Now collecting every folder or file inside
this.recentDirs = pathDir.list((current, name) -> { this.recentDirs = pathDir.list(this::isDirectoryAndNotHidden);
var dir = new File(current, name);
return (dir.isDirectory() && ! dir.isHidden()); // TODO: FIX FOR WIN ?
});
// Check that we still don't have any fuckups // Check that we still don't have any fuckups
if (this.recentDirs != null && this.recentDirs.length > subDirNo){ if (this.recentDirs != null && this.recentDirs.length > subDirNo){
Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER); Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
@ -472,11 +380,11 @@ class GoldLeaf_111 extends TransferModule {
* Handle GetFile * Handle GetFile
* @return true - failed, false - passed * @return true - failed, false - passed
* */ * */
private boolean getFile(String dirName, int subDirNo){ private boolean getFile(String glDirName, int subDirNo){
var command = new LinkedList<byte[]>(); var command = new LinkedList<byte[]>();
if (dirName.startsWith("HOME:/")) { if (glDirName.startsWith("HOME:/")) {
dirName = updateHomePath(dirName); var dirName = updateHomePath(glDirName);
if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){ if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
var fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8); var fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_8);
@ -485,23 +393,10 @@ class GoldLeaf_111 extends TransferModule {
} }
else { else {
var pathDir = new File(dirName); var pathDir = new File(dirName);
// Make sure it's exists and it's path if (notExistsOrDirectory(pathDir))
if ((! pathDir.exists() ) || (! pathDir.isDirectory()) ) // TODO: CHECK AND REMOVE .exists() IF REDUNDANT
writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]"); writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
this.recentPath = dirName; this.recentPath = dirName;
// Now collecting every folder or file inside this.recentFiles = pathDir.list(this::isFileAndNotHidden);
if (nspFilter){
this.recentFiles = pathDir.list((current, name) -> {
var dir = new File(current, name);
return (! dir.isDirectory() && name.toLowerCase().endsWith(".nsp"));
});
}
else {
this.recentFiles = pathDir.list((current, name) -> {
var dir = new File(current, name);
return (! dir.isDirectory() && (! dir.isHidden()));
});
}
// Check that we still don't have any fuckups // Check that we still don't have any fuckups
if (this.recentFiles != null && this.recentFiles.length > subDirNo){ if (this.recentFiles != null && this.recentFiles.length > subDirNo){
Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER); Arrays.sort(recentFiles, String.CASE_INSENSITIVE_ORDER);
@ -514,14 +409,14 @@ class GoldLeaf_111 extends TransferModule {
} }
return writeGL_PASS(command, "GL Handle 'GetFile' command."); return writeGL_PASS(command, "GL Handle 'GetFile' command.");
} }
else if (dirName.equals("VIRT:/") && (! nspMap.isEmpty())){ // thus nspMapKeySetIndexes also != 0 else if (glDirName.equals("VIRT:/") && (! nspMap.isEmpty())){ // thus nspMapKeySetIndexes also != 0
var fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_8); var fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_8);
command.add(intToArrLE(fileNameBytes.length)); command.add(intToArrLE(fileNameBytes.length));
command.add(fileNameBytes); command.add(fileNameBytes);
return writeGL_PASS(command, "GL Handle 'GetFile' command."); return writeGL_PASS(command, "GL Handle 'GetFile' command.");
} }
else if (dirName.equals("SPEC:/") && (selectedFile != null)){ else if (glDirName.equals("SPEC:/") && (selectedFile != null)){
byte[] fileNameBytes = selectedFile.getName().getBytes(StandardCharsets.UTF_8); var fileNameBytes = selectedFile.getName().getBytes(StandardCharsets.UTF_8);
command.add(intToArrLE(fileNameBytes.length)); command.add(intToArrLE(fileNameBytes.length));
command.add(fileNameBytes); command.add(fileNameBytes);
return writeGL_PASS(command, "GL Handle 'GetFile' command."); return writeGL_PASS(command, "GL Handle 'GetFile' command.");
@ -541,9 +436,9 @@ class GoldLeaf_111 extends TransferModule {
if (fileDirElement.exists()){ if (fileDirElement.exists()){
if (fileDirElement.isDirectory()) if (fileDirElement.isDirectory())
command.add(GL_OBJ_TYPE_DIR); command.add(GL_OBJECT_TYPE_DIR);
else { else {
command.add(GL_OBJ_TYPE_FILE); command.add(GL_OBJECT_TYPE_FILE);
command.add(longToArrLE(fileDirElement.length())); command.add(longToArrLE(fileDirElement.length()));
} }
return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName); return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName);
@ -552,7 +447,7 @@ class GoldLeaf_111 extends TransferModule {
else if (glFileName.startsWith("VIRT:/")) { else if (glFileName.startsWith("VIRT:/")) {
var fileName = glFileName.replaceFirst("^.*?:/", ""); var fileName = glFileName.replaceFirst("^.*?:/", "");
if (nspMap.containsKey(fileName)){ if (nspMap.containsKey(fileName)){
command.add(GL_OBJ_TYPE_FILE); // THIS IS INT command.add(GL_OBJECT_TYPE_FILE); // THIS IS INT
if (nspMap.get(fileName).isDirectory()) if (nspMap.get(fileName).isDirectory())
command.add(longToArrLE(splitFileSize.get(fileName))); // YES, THIS IS LONG!; command.add(longToArrLE(splitFileSize.get(fileName))); // YES, THIS IS LONG!;
else else
@ -564,7 +459,7 @@ class GoldLeaf_111 extends TransferModule {
else if (glFileName.startsWith("SPEC:/")){ else if (glFileName.startsWith("SPEC:/")){
var fileName = glFileName.replaceFirst("^.*?:/", ""); var fileName = glFileName.replaceFirst("^.*?:/", "");
if (selectedFile.getName().equals(fileName)){ if (selectedFile.getName().equals(fileName)){
command.add(GL_OBJ_TYPE_FILE); command.add(GL_OBJECT_TYPE_FILE);
command.add(longToArrLE(selectedFile.length())); command.add(longToArrLE(selectedFile.length()));
return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName); return writeGL_PASS(command, "GL Handle 'StatPath' command for "+glFileName);
} }
@ -577,7 +472,7 @@ class GoldLeaf_111 extends TransferModule {
* */ * */
private boolean rename(String glFileName, String glNewFileName){ private boolean rename(String glFileName, String glNewFileName){
if (glFileName.startsWith("HOME:/")){ if (glFileName.startsWith("HOME:/")){
// This shit takes too much time to explain, but such behaviour won't let GL to fail // Prevent GL failures
this.recentPath = null; this.recentPath = null;
this.recentFiles = null; this.recentFiles = null;
this.recentDirs = null; this.recentDirs = null;
@ -590,7 +485,9 @@ class GoldLeaf_111 extends TransferModule {
return writeGL_PASS("GL Handle 'Rename' command."); return writeGL_PASS("GL Handle 'Rename' command.");
} }
} }
catch (SecurityException ignored){} catch (SecurityException se){
return writeGL_FAIL("GL Handle 'Rename' command failed:\n\t" +se.getMessage());
}
} }
// For VIRT:/ and others we don't serve requests // For VIRT:/ and others we don't serve requests
return writeGL_FAIL("GL Handle 'Rename' command is not supported for virtual drive, selected files," + return writeGL_FAIL("GL Handle 'Rename' command is not supported for virtual drive, selected files," +
@ -600,48 +497,43 @@ class GoldLeaf_111 extends TransferModule {
* Handle 'Delete' * Handle 'Delete'
* @return true - failed, false - passed * @return true - failed, false - passed
* */ * */
private boolean delete(String fileName) { private boolean delete(String glFileName) {
if (fileName.startsWith("HOME:/")) { if (! glFileName.startsWith("HOME:/"))
fileName = updateHomePath(fileName); return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory] "+glFileName);
File fileToDel = new File(fileName); var file = new File(updateHomePath(glFileName));
try { try {
if (fileToDel.delete()){ if (file.delete())
return writeGL_PASS("GL Handle 'Rename' command."); return writeGL_PASS("GL Handle 'Rename' command.");
} }
}
catch (SecurityException ignored){} // Ah, leave it catch (SecurityException ignored){} // Ah, leave it
}
// For VIRT:/ and others we don't serve requests return writeGL_FAIL("GL Handle 'Create' command [unknown drive/read-only directory]");
return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
} }
/** /**
* Handle 'Create' * Handle 'Create'
* @param type 1 for file * @param type 1 file, 2 folder
* 2 for folder * @param glFileName full path including new file name in the end
* @param fileName full path including new file name in the end
* @return true - failed, false - passed * @return true - failed, false - passed
* */ * */
private boolean create(String fileName, byte type) { private boolean create(String glFileName, byte type) {
if (! fileName.startsWith("HOME:/")) // For VIRT:/ and others we don't serve requests if (! glFileName.startsWith("HOME:/")) // 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]"); return writeGL_FAIL("GL Handle 'Create' command [not supported for virtual drive/wrong drive/read-only directory]"+glFileName);
fileName = updateHomePath(fileName); var file = new File(updateHomePath(glFileName));
boolean result = false;
try { try {
if (type == 1) boolean result = switch (type) {
result = new File(fileName).createNewFile(); case 1 -> file.createNewFile();
else if (type == 2) case 2 -> file.mkdir();
result = new File(fileName).mkdir(); default -> false;
};
if (result)
return writeGL_PASS("GL Handle 'Create' command.");
} }
catch (SecurityException | IOException ignored){} catch (SecurityException | IOException ignored){}
if (result) { return writeGL_FAIL("GL Handle 'Create' command [unknown drive/read-only directory]");
return writeGL_PASS("GL Handle 'Create' command.");
}
return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
} }
/** /**
@ -660,11 +552,9 @@ class GoldLeaf_111 extends TransferModule {
var fNamePath = nspMap.get(fileName).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length var fNamePath = nspMap.get(fileName).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
// If we don't have this file opened, let's open it // If we don't have this file opened, let's open it
if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fNamePath))) { if (openReadFileNameAndPath == null || (! openReadFileNameAndPath.equals(fNamePath))) {
// Try close what opened if (openReadFileNameAndPath != null) // (Try to) close what opened
if (openReadFileNameAndPath != null)
closeRAFandSplitReader(); closeRAFandSplitReader();
// Open what has to be opened try{ // And open the rest
try{
var tempFile = nspMap.get(fileName); var tempFile = nspMap.get(fileName);
if (tempFile.isDirectory()) { if (tempFile.isDirectory()) {
randAccessFile = null; randAccessFile = null;
@ -713,11 +603,11 @@ class GoldLeaf_111 extends TransferModule {
if (randAccessFile == null){ if (randAccessFile == null){
splitReader.seek(offset); splitReader.seek(offset);
bytesRead = splitReader.read(chunk); // Let's find out how many bytes we got bytesRead = splitReader.read(chunk); // How many bytes we got?
} }
else { else {
randAccessFile.seek(offset); randAccessFile.seek(offset);
bytesRead = randAccessFile.read(chunk); // Let's find out how many bytes we got bytesRead = randAccessFile.read(chunk); // How many bytes we got?
} }
if (bytesRead != (int) size) // Let's check that we read expected size if (bytesRead != (int) size) // Let's check that we read expected size
@ -741,7 +631,6 @@ class GoldLeaf_111 extends TransferModule {
/** /**
* Handle 'WriteFile' * Handle 'WriteFile'
* @param fileName full path including new file name in the end * @param fileName full path including new file name in the end
*
* @return true - failed, false - passed * @return true - failed, false - passed
* */ * */
private boolean writeFile(String fileName) { private boolean writeFile(String fileName) {
@ -814,39 +703,56 @@ class GoldLeaf_111 extends TransferModule {
private String updateHomePath(String glPath){ private String updateHomePath(String glPath){
if (isWindows) if (isWindows)
glPath = glPath.replaceAll("/", "\\\\"); glPath = glPath.replaceAll("/", "\\\\");
glPath = homePath + glPath.substring(6); // Do not use replaceAll since it will consider \ as special directive glPath = homePath + glPath.substring(6); // Better not using .replaceAll() since it will consider \ as special directive
return glPath; return glPath;
} }
/**
* Convert INT (Little endian) value to bytes-array representation private boolean isFileAndNotHidden(File parent, String child){
* */ var entry = new File(parent, child);
private byte[] intToArrLE(int value){ return (! entry.isDirectory()) && (nspFilter ?
return ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN) child.toLowerCase().endsWith(".nsp") :
.putInt(value) ! entry.isHidden());
.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);
} }
//------------------------------------------------------------------------------------------------------------------ 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
*/
private void closeOpenedReadFilesGl(){
if (openReadFileNameAndPath != null){
closeRAFandSplitReader();
openReadFileNameAndPath = null;
randAccessFile = null;
splitReader = null;
}
}
private void closeRAFandSplitReader(){
closeRAF();
try{
splitReader.close();
}
catch (IOException ioe_){
print("Unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
}
catch (Exception ignored){}
}
private 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 */ /* GL READ/WRITE USB SPECIFIC */
/*----------------------------------------------------*/ /*----------------------------------------------------*/

View file

@ -0,0 +1,54 @@
/*
Copyright 2019-2025 Dmitry Isaenko
This file is part of NS-USBloader.
NS-USBloader is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
NS-USBloader is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with NS-USBloader. If not, see <https://www.gnu.org/licenses/>.
*/
package nsusbloader.com.usb.gl;
public enum GoldleafCmd {
GetDriveCount((byte) 1),
GetDriveInfo((byte) 2),
StatPath((byte) 3),
GetFileCount((byte) 4),
GetFile((byte) 5),
GetDirectoryCount((byte) 6),
GetDirectory((byte) 7),
StartFile((byte) 8),
ReadFile((byte) 9),
WriteFile((byte) 10),
EndFile((byte) 11),
Create((byte) 12),
Delete((byte) 13),
Rename((byte) 14),
GetSpecialPathCount((byte) 15),
GetSpecialPath((byte) 16),
SelectFile((byte) 17),
CMD_UNKNOWN((byte) 255);
private final byte id;
GoldleafCmd(byte id) {
this.id = id;
}
public static GoldleafCmd get(byte id) {
for(GoldleafCmd cmd : values()) {
if(cmd.id == id)
return cmd;
}
return CMD_UNKNOWN;
}
}