diff --git a/pom.xml b/pom.xml
index 72f9d15..8f21b07 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
NS-USBloader
ns-usbloader
- 0.7-SNAPSHOT
+ 0.8-SNAPSHOT
https://github.com/developersu/ns-usbloader/
@@ -218,11 +218,11 @@
1.8
- 0.7.0.0
+ 0.8.0.0
${project.version}
TinFoil and GoldLeaf installer for your NS
GNU General Public License v3, 2019 ${organization.name}. Russia/LPR.
- 0.7.0.0
+ 0.8.0.0
${project.version}
${organization.name}
${project.name}
diff --git a/src/main/java/nsusbloader/ModelControllers/LogPrinter.java b/src/main/java/nsusbloader/ModelControllers/LogPrinter.java
index 8a6edb7..4d0c34d 100644
--- a/src/main/java/nsusbloader/ModelControllers/LogPrinter.java
+++ b/src/main/java/nsusbloader/ModelControllers/LogPrinter.java
@@ -49,8 +49,11 @@ public class LogPrinter {
/**
* Update progress for progress bar
* */
- public void updateProgress(Double value) throws InterruptedException{
- progressQueue.put(value);
+ public void updateProgress(Double value) {
+ try {
+ progressQueue.put(value);
+ }
+ catch (InterruptedException ignored){} // TODO: Do something with this
}
/**
* When we're done - update status
diff --git a/src/main/java/nsusbloader/NET/NETCommunications.java b/src/main/java/nsusbloader/NET/NETCommunications.java
index 0a711c9..0a42575 100644
--- a/src/main/java/nsusbloader/NET/NETCommunications.java
+++ b/src/main/java/nsusbloader/NET/NETCommunications.java
@@ -374,23 +374,14 @@ public class NETCommunications extends Task { // todo: thows IOException?
}
currSockOS.write(byteBuf);
//-----------------------------------------/
- try {
- logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
- }catch (InterruptedException ie){
- getException().printStackTrace(); // TODO: Do something with this
- }
+ logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
//-----------------------------------------/
currentOffset += readPice;
}
currSockOS.flush(); // TODO: check if this really needed.
bis.close();
//-----------------------------------------/
- try{
- logPrinter.updateProgress(1.0);
- }
- catch (InterruptedException ie){
- getException().printStackTrace(); // TODO: Do something with this
- }
+ logPrinter.updateProgress(1.0);
//-----------------------------------------/
}
catch (IOException ioe){
diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java
index 4eb4c00..2866ee2 100644
--- a/src/main/java/nsusbloader/NSLMain.java
+++ b/src/main/java/nsusbloader/NSLMain.java
@@ -12,7 +12,7 @@ import java.util.Locale;
import java.util.ResourceBundle;
public class NSLMain extends Application {
- public static final String appVersion = "v0.7";
+ public static final String appVersion = "v0.8";
@Override
public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml"));
diff --git a/src/main/java/nsusbloader/USB/GoldLeaf.java b/src/main/java/nsusbloader/USB/GoldLeaf.java
new file mode 100644
index 0000000..1e1f46a
--- /dev/null
+++ b/src/main/java/nsusbloader/USB/GoldLeaf.java
@@ -0,0 +1,1115 @@
+package nsusbloader.USB;
+
+import javafx.application.Platform;
+import javafx.concurrent.Task;
+import javafx.stage.FileChooser;
+import nsusbloader.MediatorControl;
+import nsusbloader.ModelControllers.LogPrinter;
+import nsusbloader.NSLDataTypes.EFileStatus;
+import nsusbloader.NSLDataTypes.EMsgType;
+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 processing
+ */
+class GoldLeaf implements ITransferModule {
+ private LogPrinter logPrinter;
+ private DeviceHandle handlerNS;
+ private LinkedHashMap nspMap;
+ private boolean nspFilterForGl;
+ private Task task;
+
+ // 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, 0x64, (byte) 0xcb, 0x00, 0x00}; // used @ writeToUsb_GLCMD
+
+ // System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64
+ private final byte[] GL_OBJ_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
+ private final byte[] GL_OBJ_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
+
+ private String recentPath = null;
+ private String[] recentDirs = null;
+ private String[] recentFiles = null;
+
+ private String[] nspMapKeySetIndexes;
+
+ private String openReadFileNameAndPath;
+ private RandomAccessFile randAccessFile;
+
+ private HashMap writeFilesMap;
+
+ private boolean isWindows;
+ private String homePath;
+ // For using in CMD_SelectFile with SPEC:/ prefix
+ private File selectedFile;
+
+ GoldLeaf(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter, boolean nspFilter){
+ final byte CMD_GetDriveCount = 0x00;
+ final byte CMD_GetDriveInfo = 0x01;
+ final byte CMD_StatPath = 0x02; // proxy done [proxy: in case if folder contains ENG+RUS+UKR file names works incorrect]
+ final byte CMD_GetFileCount = 0x03;
+ final byte CMD_GetFile = 0x04; // proxy done
+ final byte CMD_GetDirectoryCount = 0x05;
+ final byte CMD_GetDirectory = 0x06; // proxy done
+ final byte CMD_ReadFile = 0x07; // no way to do poxy
+ final byte CMD_WriteFile = 0x08; // add predictable behavior
+ final byte CMD_Create = 0x09;
+ final byte CMD_Delete = 0x0a;//10
+ final byte CMD_Rename = 0x0b;//11
+ final byte CMD_GetSpecialPathCount = 0x0c;//12 // Special folders count; simplified usage @ NS-UL
+ final byte CMD_GetSpecialPath = 0x0d;//13 // Information about special folders; simplified usage @ NS-UL
+ final byte CMD_SelectFile = 0x0e;//14
+ final byte CMD_Max = 0x0f;//15 // not used @ NS-UL & GT
+
+ final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49};
+
+ this.handlerNS = handler;
+ this.nspMap = nspMap;
+ this.logPrinter = logPrinter;
+ this.nspFilterForGl = nspFilter;
+ this.task = task;
+
+ logPrinter.print("============= GoldLeaf =============\n\tVIRT:/ equals files added into the application\n\tHOME:/ equals "
+ +System.getProperty("user.home"), EMsgType.INFO);
+
+ // Let's collect file names to the array to simplify our life
+ writeFilesMap = new HashMap<>();
+ int i = 0;
+ nspMapKeySetIndexes = new String[nspMap.size()];
+ for (String fileName : nspMap.keySet())
+ nspMapKeySetIndexes[i++] = fileName;
+
+ isWindows = System.getProperty("os.name").contains("Windows");
+
+ homePath = System.getProperty("user.home")+File.separator;
+
+ // Go parse commands
+ byte[] readByte;
+ int someLength1,
+ someLength2;
+ main_loop:
+ while (true) { // Till user interrupted process.
+ readByte = readGL();
+
+ if (readByte == null) // Issue @ readFromUsbGL method
+ return;
+
+ //RainbowHexDump.hexDumpUTF16LE(readByte); // DEBUG
+ //System.out.println("CHOICE: "+readByte[4]); // DEBUG
+
+ if (Arrays.equals(Arrays.copyOfRange(readByte, 0,4), CMD_GLCI)) {
+ switch (readByte[4]) {
+ case CMD_GetDriveCount:
+ if (getDriveCount())
+ break main_loop;
+ break;
+ case CMD_GetDriveInfo:
+ if (getDriveInfo(arrToIntLE(readByte,8)))
+ break main_loop;
+ break;
+ case CMD_GetSpecialPathCount:
+ if (getSpecialPathCount())
+ break main_loop;
+ break;
+ case CMD_GetSpecialPath:
+ if (getSpecialPath(arrToIntLE(readByte,8)))
+ break main_loop;
+ break;
+ case CMD_GetDirectoryCount:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), true))
+ break main_loop;
+ break;
+ case CMD_GetFileCount:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), false))
+ break main_loop;
+ break;
+ case CMD_GetDirectory:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), arrToIntLE(readByte, someLength1+12)))
+ break main_loop;
+ break;
+ case CMD_GetFile:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), arrToIntLE(readByte, someLength1+12)))
+ break main_loop;
+ break;
+ case CMD_StatPath:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE)))
+ break main_loop;
+ break;
+ case CMD_Rename:
+ someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
+ someLength2 = arrToIntLE(readByte, 16+someLength1) * 2; // Since GL 0.7
+ if (rename(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE),
+ new String(readByte, 16+someLength1+4, someLength2, StandardCharsets.UTF_16LE)))
+ break main_loop;
+ break;
+ case CMD_Delete:
+ someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
+ if (delete(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE)))
+ break main_loop;
+ break;
+ case CMD_Create:
+ someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
+ if (create(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE), readByte[8]))
+ break main_loop;
+ break;
+ case CMD_ReadFile:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE),
+ arrToLongLE(readByte, 12+someLength1),
+ arrToLongLE(readByte, 12+someLength1+8)))
+ break main_loop;
+ break;
+ case CMD_WriteFile:
+ someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
+ if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE),
+ arrToLongLE(readByte, 12+someLength1)))
+ break main_loop;
+ break;
+ case CMD_SelectFile:
+ if (selectFile())
+ break main_loop;
+ break;
+ default:
+ writeGL_FAIL("GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
+ }
+ }
+ }
+ // Close (and flush) all opened streams.
+ if (writeFilesMap.size() != 0){
+ for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
+ try{
+ fBufOutStream.close();
+ }catch (IOException ignored){}
+ }
+ }
+ closeOpenedReadFilesGl();
+ }
+
+ /**
+ * Close files opened for read/write
+ */
+ private void closeOpenedReadFilesGl(){
+ if (openReadFileNameAndPath != null){ // Perfect time to close our opened files
+ try{
+ randAccessFile.close();
+ }
+ catch (IOException ignored){}
+ openReadFileNameAndPath = null;
+ randAccessFile = null;
+ }
+ }
+ /**
+ * Handle GetDriveCount
+ * @return true if failed
+ * false if everything is ok
+ */
+ private boolean getDriveCount(){
+ // Let's declare 2 drives
+ byte[] drivesCnt = intToArrLE(2); //2
+ // Write count of drives
+ if (writeGL_PASS(drivesCnt)) {
+ logPrinter.print("GL Handle 'ListDrives' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Handle GetDriveInfo
+ * @return true if failed
+ * false if everything is ok
+ */
+ private boolean getDriveInfo(int driveNo){
+ if (driveNo < 0 || driveNo > 1){
+ return writeGL_FAIL("GL Handle 'GetDriveInfo' command [no such drive]");
+ }
+
+ byte[] driveLabel,
+ driveLabelLen,
+ driveLetter,
+ driveLetterLen,
+ totalFreeSpace,
+ totalSize;
+ long totalSizeLong = 0;
+
+ // 0 == VIRTUAL DRIVE
+ if (driveNo == 0){
+ driveLabel = "Virtual".getBytes(StandardCharsets.UTF_16LE);
+ driveLabelLen = intToArrLE(driveLabel.length / 2); // since GL 0.7
+ driveLetter = "VIRT".getBytes(StandardCharsets.UTF_16LE); // TODO: Consider moving to class field declaration
+ driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
+ totalFreeSpace = new byte[4];
+ for (File nspFile : nspMap.values()){
+ totalSizeLong += nspFile.length();
+ }
+ totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
+ }
+ else { //1 == User home dir
+ driveLabel = "Home".getBytes(StandardCharsets.UTF_16LE);
+ driveLabelLen = intToArrLE(driveLabel.length / 2);// since GL 0.7
+ driveLetter = "HOME".getBytes(StandardCharsets.UTF_16LE);
+ driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
+ File userHomeDir = new File(System.getProperty("user.home"));
+ long totalFreeSpaceLong = userHomeDir.getFreeSpace();
+ totalFreeSpace = Arrays.copyOfRange(longToArrLE(totalFreeSpaceLong), 0, 4); // Dirty hack; now for GL!;
+ totalSizeLong = userHomeDir.getTotalSpace();
+ totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
+
+ //System.out.println("totalSize: "+totalSizeLong+"totalFreeSpace: "+totalFreeSpaceLong);
+ }
+
+ 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)) {
+ logPrinter.print("GL Handle 'GetDriveInfo' command", EMsgType.FAIL);
+ return true;
+ }
+
+ return false;
+ }
+ /**
+ * Handle SpecialPathCount
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean getSpecialPathCount(){
+ // Let's declare nothing =)
+ byte[] specialPathCnt = intToArrLE(0);
+ // Write count of special paths
+ if (writeGL_PASS(specialPathCnt)) {
+ logPrinter.print("GL Handle 'SpecialPathCount' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ /**
+ * Handle SpecialPath
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean getSpecialPath(int specialPathNo){
+ return writeGL_FAIL("GL Handle 'SpecialPath' command [not supported]");
+ }
+ /**
+ * Handle GetDirectoryCount & GetFileCount
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean getDirectoryOrFileCount(String path, boolean isGetDirectoryCount) {
+ if (path.equals("VIRT:/")) {
+ if (isGetDirectoryCount){
+ if (writeGL_PASS()) {
+ logPrinter.print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else {
+ if (writeGL_PASS(intToArrLE(nspMap.size()))) {
+ logPrinter.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()) {
+ logPrinter.print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ // Sorting is mandatory TODO: 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))) {
+ logPrinter.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()) {
+ logPrinter.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))) {
+ logPrinter.print("GL Handle 'GetFileCount' command Count = 1", EMsgType.FAIL);
+ return true;
+ }
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+path);
+ }
+ else { // If requested drive is not VIRT and not HOME then reply error
+ return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+path);
+ }
+ return false;
+ }
+ /**
+ * Handle GetDirectory
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean getDirectory(String dirName, int subDirNo){
+ if (dirName.startsWith("HOME:/")) {
+ dirName = updateHomePath(dirName);
+
+ List command = new LinkedList<>();
+
+ if (dirName.equals(recentPath) && recentDirs != null && recentDirs.length != 0){
+ byte[] dirNameBytes = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_16LE);
+
+ command.add(intToArrLE(dirNameBytes.length / 2)); // Since GL 0.7
+ 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_16LE);
+ command.add(intToArrLE(dirBytesName.length / 2)); // Since GL 0.7
+ command.add(dirBytesName);
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
+ }
+ //if (proxyForGL) // TODO: NOTE: PROXY TAILS
+ // return proxyGetDirFile(true);
+
+ if (writeGL_PASS(command)) {
+ logPrinter.print("GL Handle 'GetDirectory' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ // VIRT:// and any other
+ return writeGL_FAIL("GL Handle 'GetDirectory' command for virtual drive [no folders support]");
+ }
+ /**
+ * Handle GetFile
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean getFile(String dirName, int subDirNo){
+ List command = new LinkedList<>();
+
+ if (dirName.startsWith("HOME:/")) {
+ dirName = updateHomePath(dirName);
+
+ if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
+ byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_16LE);
+
+ command.add(intToArrLE(fileNameBytes.length / 2)); //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_16LE);
+ command.add(intToArrLE(fileNameBytes.length / 2)); //Since GL 0.7
+ command.add(fileNameBytes);
+ }
+ else
+ return writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
+ }
+ //if (proxyForGL) // TODO: NOTE: PROXY TAILS
+ // return proxyGetDirFile(false);
+ if (writeGL_PASS(command)) {
+ logPrinter.print("GL Handle 'GetFile' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ else if (dirName.equals("VIRT:/")){
+ if (nspMap.size() != 0){ // therefore nspMapKeySetIndexes also != 0
+ byte[] fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_16LE);
+ command.add(intToArrLE(fileNameBytes.length / 2)); // since GL 0.7
+ command.add(fileNameBytes);
+ if (writeGL_PASS(command)) {
+ logPrinter.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_16LE);
+ command.add(intToArrLE(fileNameBytes.length / 2)); // since GL 0.7
+ command.add(fileNameBytes);
+ if (writeGL_PASS(command)) {
+ logPrinter.print("GL Handle 'GetFile' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ // any other cases
+ return writeGL_FAIL("GL Handle 'GetFile' command for virtual drive [no folders support?]");
+ }
+ /**
+ * Handle StatPath
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean statPath(String filePath){
+ List command = new LinkedList<>();
+
+ if (filePath.startsWith("HOME:/")){
+ filePath = updateHomePath(filePath);
+ //if (proxyForGL) // TODO:NOTE PROXY TAILS
+ // return proxyStatPath(filePath); // dirty name
+
+ File fileDirElement = new File(filePath);
+ if (fileDirElement.exists()){
+ if (fileDirElement.isDirectory())
+ command.add(GL_OBJ_TYPE_DIR);
+ else {
+ command.add(GL_OBJ_TYPE_FILE);
+ command.add(longToArrLE(fileDirElement.length()));
+ }
+ if (writeGL_PASS(command)) {
+ logPrinter.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
+ command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
+ if (writeGL_PASS(command)) {
+ logPrinter.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)) {
+ logPrinter.print("GL Handle 'StatPath' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ return writeGL_FAIL("GL Handle 'StatPath' command [no such folder] - "+filePath);
+ }
+ /**
+ * Handle 'Rename' that is actually 'mv'
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean rename(String fileName, String newFileName){
+ if (fileName.startsWith("HOME:/")){
+ // This shit takes too much time to explain, but such behaviour won't let GL to fail
+ this.recentPath = null;
+ this.recentFiles = null;
+ this.recentDirs = null;
+ fileName = updateHomePath(fileName);
+ newFileName = updateHomePath(newFileName);
+
+ File currentFile = new File(fileName);
+ File newFile = new File(newFileName);
+ if (! newFile.exists()){ // Else, report error
+ try {
+ if (currentFile.renameTo(newFile)){
+ if (writeGL_PASS()) {
+ logPrinter.print("GL Handle 'Rename' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (SecurityException ignored){} // Ah, leave it
+ }
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Rename' command [not supported for virtual drive/wrong drive/file with such name already exists/read-only directory]");
+ }
+ /**
+ * Handle 'Delete'
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean delete(String fileName) {
+ if (fileName.startsWith("HOME:/")) {
+ fileName = updateHomePath(fileName);
+
+ File fileToDel = new File(fileName);
+ try {
+ if (fileToDel.delete()){
+ if (writeGL_PASS()) {
+ logPrinter.print("GL Handle 'Rename' command.", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+ catch (SecurityException ignored){} // Ah, leave it
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
+ }
+ /**
+ * Handle 'Create'
+ * @param type 1 for file
+ * 2 for folder
+ * @param fileName full path including new file name in the end
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean create(String fileName, byte type) {
+ if (fileName.startsWith("HOME:/")) {
+ fileName = updateHomePath(fileName);
+ File fileToCreate = new File(fileName);
+ boolean result = false;
+ if (type == 1){
+ try {
+ result = fileToCreate.createNewFile();
+ }
+ catch (SecurityException | IOException ignored){}
+ }
+ else if (type == 2){
+ try {
+ result = fileToCreate.mkdir();
+ }
+ catch (SecurityException ignored){}
+ }
+ if (result){
+ if (writeGL_PASS()) {
+ logPrinter.print("GL Handle 'Create' command.", EMsgType.FAIL);
+ return true;
+ }
+ //logPrinter.print("GL Handle 'Create' command.", EMsgType.PASS);
+ return false;
+ }
+ }
+ // For VIRT:/ and others we don't serve requests
+ return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
+ }
+
+ /**
+ * Handle 'ReadFile'
+ * @param fileName full path including new file name in the end
+ * @param offset requested offset
+ * @param size requested size
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean readFile(String fileName, long offset, long size) {
+ //System.out.println("readFile "+fileName+" "+offset+" "+size);
+ 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 (IOException ignored){}
+ }
+ // Open what has to be opened
+ try{
+ randAccessFile = new RandomAccessFile(nspMap.get(fileName.substring(6)), "r");
+ openReadFileNameAndPath = fNamePath;
+ }
+ catch (IOException 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 ignored){}
+ }
+ // Open what has to be opened
+ try{
+ randAccessFile = new RandomAccessFile(fileName, "r");
+ openReadFileNameAndPath = fileName;
+ }catch (IOException ioe){
+ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
+ }
+ }
+ }
+ //----------------------- Actual transfer chain ------------------------
+ try{
+ randAccessFile.seek(offset);
+ byte[] chunk = new byte[(int)size]; // WTF MAN?
+ // Let's find out how much bytes we got
+ int bytesRead = randAccessFile.read(chunk);
+ // Let's check that we read expected size
+ if (bytesRead != (int)size)
+ return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
+ // Let's tell as a command about our result.
+ if (writeGL_PASS(longToArrLE(size))) {
+ logPrinter.print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
+ return true;
+ }
+ // Let's bypass bytes we read total
+ if (writeToUsb(chunk)) {
+ logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ catch (IOException ioe){
+ try{
+ randAccessFile.close();
+ }
+ catch (IOException ioee){
+ logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioee.getMessage(), EMsgType.WARNING);
+ }
+ openReadFileNameAndPath = null;
+ randAccessFile = 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
+ * @param size requested size
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean writeFile(String fileName, long size) {
+ //System.out.println("writeFile "+fileName+" "+size);
+ if (fileName.startsWith("VIRT:/")){
+ return writeGL_FAIL("GL Handle 'WriteFile' command [not supported for virtual drive]");
+ }
+ else {
+ fileName = updateHomePath(fileName);
+ // Check if we didn't see this (or any) file during this session
+ if (writeFilesMap.size() == 0 || (! writeFilesMap.containsKey(fileName))){
+ // Open what we have to open
+ File writeFile = new File(fileName);
+ // If this file exists GL will take care
+ // Otherwise, let's add it
+ try{
+ BufferedOutputStream writeFileBufOutStream = new BufferedOutputStream(new FileOutputStream(writeFile, true));
+ writeFilesMap.put(fileName, writeFileBufOutStream);
+ } catch (IOException ioe){
+ return writeGL_FAIL("GL Handle 'WriteFile' command [IOException]\n\t"+ioe.getMessage());
+ }
+ }
+ // Now we have stream
+ BufferedOutputStream myStream = writeFilesMap.get(fileName);
+
+ byte[] transferredData;
+
+ if ((transferredData = readGL_file()) == null){
+ logPrinter.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()) {
+ logPrinter.print("GL Handle 'WriteFile' command", EMsgType.FAIL);
+ return true;
+ }
+ return false;
+ }
+ }
+
+ /**
+ * Handle 'SelectFile'
+ * @return true if failed
+ * false if everything is ok
+ * */
+ private boolean selectFile(){
+ File selectedFile = CompletableFuture.supplyAsync(() -> {
+ FileChooser fChooser = new FileChooser();
+ fChooser.setTitle(MediatorControl.getInstance().getContoller().getResourceBundle().getString("btn_OpenFile")); // TODO: FIX BAD IMPLEMENTATION
+ fChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: Consider fixing; not a prio.
+ fChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("*", "*"));
+ return fChooser.showOpenDialog(null); // Leave as is for now.
+ }, Platform::runLater).join();
+
+ if (selectedFile != null){
+ List command = new LinkedList<>();
+ byte[] selectedFileNameBytes = ("SPEC:/"+selectedFile.getName()).getBytes(StandardCharsets.UTF_16LE);
+ command.add(intToArrLE(selectedFileNameBytes.length / 2)); // since GL 0.7
+ command.add(selectedFileNameBytes);
+ if (writeGL_PASS(command)) {
+ logPrinter.print("GL Handle 'SelectFile' command", EMsgType.FAIL);
+ this.selectedFile = null;
+ return true;
+ }
+ this.selectedFile = selectedFile;
+ return false;
+ }
+ // Nothing selected; Report failure.
+ this.selectedFile = null;
+ return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
+ }
+
+ @Override
+ public EFileStatus getStatus() {
+ return EFileStatus.UNKNOWN;
+ }
+ /*----------------------------------------------------*/
+ /* GL HELPERS */
+ /*----------------------------------------------------*/
+ /**
+ * Convert path received from GL to normal
+ */
+ private String updateHomePath(String glPath){
+ if (isWindows)
+ glPath = glPath.replaceAll("/", "\\\\");
+ glPath = homePath+glPath.substring(6); // Do not use replaceAll since it will consider \ as special directive
+ return glPath;
+ }
+ /**
+ * Convert INT (Little endian) value to bytes-array representation
+ * */
+ private byte[] intToArrLE(int value){
+ ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ byteBuffer.putInt(value);
+ return byteBuffer.array();
+ }
+ /**
+ * Convert LONG (Little endian) value to bytes-array representation
+ * */
+ private byte[] longToArrLE(long value){
+ ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN);
+ byteBuffer.putLong(value);
+ return byteBuffer.array();
+ }
+ /**
+ * Convert bytes-array to INT value (Little endian)
+ * */
+ private int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
+ }
+ /**
+ * Convert bytes-array to LONG value (Little endian)
+ * */
+ private long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
+ return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
+ }
+
+ //------------------------------------------------------------------------------------------------------------------
+
+ /*----------------------------------------------------*/
+ /* GL READ/WRITE USB SPECIFIC */
+ /*----------------------------------------------------*/
+
+ private byte[] readGL(){
+ ByteBuffer readBuffer;
+ readBuffer = ByteBuffer.allocateDirect(4096); // GL really?
+
+ IntBuffer readBufTransferred = IntBuffer.allocate(1);
+
+ int result;
+
+ while (! task.isCancelled()) {
+ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ int trans = readBufTransferred.get();
+ byte[] receivedBytes = new byte[trans];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ closeOpenedReadFilesGl(); // Could be a problem if GL glitches and slow down process. Or if user has extra-slow SD card. TODO: refactor
+ continue;
+ default:
+ logPrinter.print("GL Data transfer issue [read]\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ logPrinter.print("GL Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ logPrinter.print("GL Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+ private byte[] readGL_file(){
+ ByteBuffer readBuffer;
+ readBuffer = ByteBuffer.allocateDirect(8388608); // Just don't ask..
+
+ IntBuffer readBufTransferred = IntBuffer.allocate(1);
+
+ int result;
+
+ while (! task.isCancelled()) {
+ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ int trans = readBufTransferred.get();
+ byte[] receivedBytes = new byte[trans];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ logPrinter.print("GL Data transfer issue [read]\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ logPrinter.print("GL Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ logPrinter.print("GL Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+ /**
+ * Write new command. Shitty implementation.
+ * */
+ private boolean writeGL_PASS(byte[] message){
+ ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(message);
+ return writeToUsb(writeBuffer.array());
+ }
+ private boolean writeGL_PASS(){
+ return writeToUsb(Arrays.copyOf(CMD_GLCO_SUCCESS, 4096));
+ }
+ private boolean writeGL_PASS(List messages){
+ ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ for (byte[] arr : messages)
+ writeBuffer.put(arr);
+ return writeToUsb(writeBuffer.array());
+ }
+
+ private boolean writeGL_FAIL(String reportToUImsg){
+ if (writeToUsb(Arrays.copyOf(CMD_GLCO_FAILURE, 4096))){
+ logPrinter.print(reportToUImsg, EMsgType.WARNING);
+ return true;
+ }
+ logPrinter.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){
+ ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
+ writeBuffer.put(message); // Don't do writeBuffer.rewind();
+ IntBuffer writeBufTransferred = IntBuffer.allocate(1);
+ int result;
+
+ while (! task.isCancelled()) {
+ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
+
+ switch (result){
+ case LibUsb.SUCCESS:
+ if (writeBufTransferred.get() == message.length)
+ return false;
+ else {
+ logPrinter.print("GL Data transfer issue [write]\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
+ return true;
+ }
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ logPrinter.print("GL Data transfer issue [write]\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ logPrinter.print("GL Execution stopped", EMsgType.FAIL);
+ return true;
+ }
+ }
+ logPrinter.print("GL Execution interrupted", EMsgType.INFO);
+ return true;
+ }
+
+ /*----------------------------------------------------*/
+ /* GL EXPERIMENTAL PART */
+ /* (left for better times) */
+ /*----------------------------------------------------*/
+ /*
+ private boolean proxyStatPath(String path) {
+ ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
+ List fileBytesSize = new LinkedList<>();
+ if ((recentDirs.length == 0) && (recentFiles.length == 0)) {
+ return writeGL_FAIL("proxyStatPath");
+ }
+ if (recentDirs.length > 0){
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(GL_OBJ_TYPE_DIR);
+ byte[] resultingDir = writeBuffer.array();
+ writeToUsb(resultingDir);
+ for (int i = 1; i < recentDirs.length; i++) {
+ readGL();
+ writeToUsb(resultingDir);
+ }
+ }
+ if (recentFiles.length > 0){
+ path = path.replaceAll(recentDirs[0]+"$", ""); // Remove the name from path
+ for (String fileName : recentFiles){
+ File f = new File(path+fileName);
+ fileBytesSize.add(longToArrLE(f.length()));
+ }
+ writeBuffer.clear();
+ for (int i = 0; i < recentFiles.length; i++){
+ readGL();
+ writeBuffer.clear();
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(GL_OBJ_TYPE_FILE);
+ writeBuffer.put(fileBytesSize.get(i));
+ writeToUsb(writeBuffer.array());
+ }
+ }
+ return false;
+ }
+
+ private boolean proxyGetDirFile(boolean forDirs){
+ ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
+ List dirBytesNameSize = new LinkedList<>();
+ List dirBytesName = new LinkedList<>();
+ if (forDirs) {
+ if (recentDirs.length <= 0)
+ return writeGL_FAIL("proxyGetDirFile");
+ for (String dirName : recentDirs) {
+ byte[] name = dirName.getBytes(StandardCharsets.UTF_16LE);
+ dirBytesNameSize.add(intToArrLE(name.length));
+ dirBytesName.add(name);
+ }
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(dirBytesNameSize.get(0));
+ writeBuffer.put(dirBytesName.get(0));
+ writeToUsb(writeBuffer.array());
+ writeBuffer.clear();
+ for (int i = 1; i < recentDirs.length; i++){
+ readGL();
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(dirBytesNameSize.get(i));
+ writeBuffer.put(dirBytesName.get(i));
+ writeToUsb(writeBuffer.array());
+ writeBuffer.clear();
+ }
+ }
+ else {
+ if (recentDirs.length <= 0)
+ return writeGL_FAIL("proxyGetDirFile");
+ for (String dirName : recentFiles){
+ byte[] name = dirName.getBytes(StandardCharsets.UTF_16LE);
+ dirBytesNameSize.add(intToArrLE(name.length));
+ dirBytesName.add(name);
+ }
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(dirBytesNameSize.get(0));
+ writeBuffer.put(dirBytesName.get(0));
+ writeToUsb(writeBuffer.array());
+ writeBuffer.clear();
+ for (int i = 1; i < recentFiles.length; i++){
+ readGL();
+ writeBuffer.put(CMD_GLCO_SUCCESS);
+ writeBuffer.put(dirBytesNameSize.get(i));
+ writeBuffer.put(dirBytesName.get(i));
+ writeToUsb(writeBuffer.array());
+ writeBuffer.clear();
+ }
+ }
+ return false;
+ }
+ */
+}
diff --git a/src/main/java/nsusbloader/USB/ITransferModule.java b/src/main/java/nsusbloader/USB/ITransferModule.java
new file mode 100644
index 0000000..2b053a7
--- /dev/null
+++ b/src/main/java/nsusbloader/USB/ITransferModule.java
@@ -0,0 +1,7 @@
+package nsusbloader.USB;
+
+import nsusbloader.NSLDataTypes.EFileStatus;
+
+public interface ITransferModule {
+ EFileStatus getStatus();
+}
diff --git a/src/main/java/nsusbloader/USB/TinFoil.java b/src/main/java/nsusbloader/USB/TinFoil.java
new file mode 100644
index 0000000..4a73da9
--- /dev/null
+++ b/src/main/java/nsusbloader/USB/TinFoil.java
@@ -0,0 +1,321 @@
+package nsusbloader.USB;
+
+import javafx.concurrent.Task;
+import nsusbloader.ModelControllers.LogPrinter;
+import nsusbloader.NSLDataTypes.EFileStatus;
+import nsusbloader.NSLDataTypes.EMsgType;
+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.Arrays;
+import java.util.LinkedHashMap;
+
+/**
+ * Tinfoil processing
+ * */
+class TinFoil implements ITransferModule {
+ private LogPrinter logPrinter;
+ private DeviceHandle handlerNS;
+ private LinkedHashMap nspMap;
+ private EFileStatus status = EFileStatus.FAILED;
+ private Task task;
+
+ TinFoil(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter){
+ this.handlerNS = handler;
+ this.nspMap = nspMap;
+ this.task = task;
+ this.logPrinter = logPrinter;
+
+ logPrinter.print("============= TinFoil =============", EMsgType.INFO);
+
+ if (!sendListOfNSP())
+ return;
+
+ if (proceedCommands()) // REPORT SUCCESS
+ status = EFileStatus.UPLOADED; // Don't change status that is already set to FAILED
+ }
+ /**
+ * Send what NSP will be transferred
+ * */
+ private boolean sendListOfNSP(){
+ // Send list of NSP files:
+ // Proceed "TUL0"
+ if (writeUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30}
+ logPrinter.print("TF Send list of files: handshake", EMsgType.FAIL);
+ return false;
+ }
+ else
+ logPrinter.print("TF Send list of files: handshake", EMsgType.PASS);
+ //Collect file names
+ StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
+ for(String nspFileName: nspMap.keySet()) {
+ nspListNamesBuilder.append(nspFileName); // And here we come with java string default encoding (UTF-16)
+ nspListNamesBuilder.append('\n');
+ }
+
+ byte[] nspListNames = nspListNamesBuilder.toString().getBytes(StandardCharsets.UTF_8);
+ ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
+ byteBuffer.putInt(nspListNames.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
+ byte[] nspListSize = byteBuffer.array(); // TODO: rewind? not sure..
+ //byteBuffer.reset();
+
+ // Sending NSP list
+ logPrinter.print("TF Send list of files", EMsgType.INFO);
+ if (writeUsb(nspListSize)) { // size of the list we're going to transfer goes...
+ logPrinter.print(" [send list length]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [send list length]", EMsgType.PASS);
+
+ if (writeUsb(new byte[8])) { // 8 zero bytes goes...
+ logPrinter.print(" [send padding]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [send padding]", EMsgType.PASS);
+
+ if (writeUsb(nspListNames)) { // list of the names goes...
+ logPrinter.print(" [send list itself]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [send list itself]", EMsgType.PASS);
+
+ return true;
+ }
+ /**
+ * After we sent commands to NS, this chain starts
+ * */
+ private boolean proceedCommands(){
+ logPrinter.print("TF Awaiting for NS commands.", EMsgType.INFO);
+
+ /* byte[] magic = new byte[4];
+ ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic); // Let's rephrase this 'string'
+ */
+ final byte[] magic = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // eq. 'TUC0' @ UTF-8 (actually ASCII lol, u know what I mean)
+
+ byte[] receivedArray;
+
+ while (true){ // Check if user interrupted process.
+
+ receivedArray = readUsb();
+
+ if (receivedArray == null) // catches error
+ return false;
+
+ if (!Arrays.equals(Arrays.copyOfRange(receivedArray, 0,4), magic)) // Bytes from 0 to 3 should contain 'magic' TUC0, so must be verified like this
+ continue;
+
+ // 8th to 12th(explicits) bytes in returned data stands for command ID as unsigned integer (Little-endian). Actually, we have to compare arrays here, but in real world it can't be greater then 0/1/2, thus:
+ // BTW also protocol specifies 4th byte to be 0x00 kinda indicating that that this command is valid. But, as you may see, never happens other situation when it's not = 0.
+ if (receivedArray[8] == 0x00){ //0x00 - exit
+ logPrinter.print("TF Received 'EXIT' command. Terminating.", EMsgType.PASS);
+ return true; // All interaction with USB device should be ended (expected);
+ }
+ else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack).
+ logPrinter.print("TF Received 'FILE RANGE' command. Proceeding: [0x0"+receivedArray[8]+"]", EMsgType.PASS);
+ /*// We can get in this pocket a length of file name (+32). Why +32? I dunno man.. Do we need this? Definitely not. This app can live without it.
+ long receivedSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 12,20)).order(ByteOrder.LITTLE_ENDIAN).getLong();
+ logsArea.appendText("[V] Received FILE_RANGE command. Size: "+Long.toUnsignedString(receivedSize)+"\n"); // this shit returns string that will be chosen next '+32'. And, BTW, can't be greater then 512
+ */
+ if (! fileRangeCmd())
+ return false; // catches exception
+ }
+ }
+ }
+ /**
+ * This is what returns requested file (files)
+ * Executes multiple times
+ * @return 'true' if everything is ok
+ * 'false' is error/exception occurs
+ * */
+ private boolean fileRangeCmd(){
+ byte[] receivedArray;
+ // Here we take information of what other side wants
+ receivedArray = readUsb();
+ if (receivedArray == null)
+ return false;
+
+ // range_offset of the requested file. In the begining it will be 0x10.
+ long receivedRangeSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 0,8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
+ byte[] receivedRangeSizeRAW = Arrays.copyOfRange(receivedArray, 0,8); // used (only) when we use sendResponse(). It's just simply.
+ long receivedRangeOffset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
+ /* Below, it's REAL NSP file name length that we sent before among others (WITHOUT +32 byes). It can't be greater then... see what is written in the beginning of this code.
+ We don't need this since in next pocket we'll get name itself UTF-8 encoded. Could be used to double-checks or something like that.
+ long receivedNspNameLen = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 16,24)).order(ByteOrder.LITTLE_ENDIAN).getLong(); */
+
+ // Requesting UTF-8 file name required:
+ receivedArray = readUsb();
+ if (receivedArray == null)
+ return false;
+
+ String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8);
+ logPrinter.print("TF Reply to requested file: "+receivedRequestedNSP
+ +"\n Range Size: "+receivedRangeSize
+ +"\n Range Offset: "+receivedRangeOffset, EMsgType.INFO);
+
+ // Sending response header
+ if (!sendResponse(receivedRangeSizeRAW)) // Get receivedRangeSize in 'RAW' format exactly as it has been received. It's simply.
+ return false;
+
+ try {
+
+ BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
+ byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
+
+ if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
+ logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
+ return false;
+ }
+
+ long currentOffset = 0;
+ // 'End Offset' equal to receivedRangeSize.
+ int readPice = 8388608; // = 8Mb
+
+ while (currentOffset < receivedRangeSize){
+ if ((currentOffset + readPice) >= receivedRangeSize )
+ readPice = Math.toIntExact(receivedRangeSize - currentOffset);
+ //System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // NOTE: DEBUG
+ // updating progress bar (if a lot of data requested) START BLOCK
+ //-----------------------------------------/
+ logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
+ //-----------------------------------------/
+ bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
+
+ if (bufferedInStream.read(bufferCurrent) != readPice) { // changed since @ v0.3.2
+ logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
+ return false;
+ }
+ //write to USB
+ if (writeUsb(bufferCurrent)) {
+ logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
+ return false;
+ }
+ currentOffset += readPice;
+ }
+ bufferedInStream.close();
+ //-----------------------------------------/
+ logPrinter.updateProgress(1.0);
+ //-----------------------------------------/
+ } catch (FileNotFoundException fnfe){
+ logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL);
+ fnfe.printStackTrace();
+ return false;
+ } catch (IOException ioe){
+ logPrinter.print("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL);
+ ioe.printStackTrace();
+ return false;
+ } catch (ArithmeticException ae){
+ logPrinter.print("TF ArithmeticException (can't cast 'offset end' - 'offsets current' to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL);
+ ae.printStackTrace();
+ return false;
+ }
+
+ return true;
+ }
+ /**
+ * Send response header.
+ * @return true if everything OK
+ * false if failed
+ * */
+ private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
+ logPrinter.print("TF Sending response", EMsgType.INFO);
+ if (writeUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0'
+ (byte) 0x01, // CMD_TYPE_RESPONSE = 1
+ (byte) 0x00, (byte) 0x00, (byte) 0x00, // kinda padding. Guys, didn't you want to use integer value for CMD semantic?
+ (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00} ) // Send integer value of '1' in Little-endian format.
+ ){
+ logPrinter.print(" [1/3]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [1/3]", EMsgType.PASS);
+ if(writeUsb(rangeSize)) { // Send EXACTLY what has been received
+ logPrinter.print(" [2/3]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [2/3]", EMsgType.PASS);
+ if(writeUsb(new byte[12])) { // kinda another one padding
+ logPrinter.print(" [3/3]", EMsgType.FAIL);
+ return false;
+ }
+ logPrinter.print(" [3/3]", EMsgType.PASS);
+ return true;
+ }
+
+ /**
+ * Sending any byte array to USB device
+ * @return 'false' if no issues
+ * 'true' if errors happened
+ * */
+ private boolean writeUsb(byte[] message){
+ ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
+ writeBuffer.put(message); // Don't do writeBuffer.rewind();
+ IntBuffer writeBufTransferred = IntBuffer.allocate(1);
+ int result;
+
+ while (! task.isCancelled()) {
+ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
+
+ switch (result){
+ case LibUsb.SUCCESS:
+ if (writeBufTransferred.get() == message.length)
+ return false;
+ else {
+ logPrinter.print("TF Data transfer issue [write]\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
+ return true;
+ }
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ logPrinter.print("TF Data transfer issue [write]\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ logPrinter.print("TF Execution stopped", EMsgType.FAIL);
+ return true;
+ }
+ }
+ logPrinter.print("TF Execution interrupted", EMsgType.INFO);
+ return true;
+ }
+ /**
+ * Reading what USB device responded.
+ * @return byte array if data read successful
+ * 'null' if read failed
+ * */
+ private byte[] readUsb(){
+ ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);
+ // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
+ IntBuffer readBufTransferred = IntBuffer.allocate(1);
+
+ int result;
+ while (! task.isCancelled()) {
+ result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
+
+ switch (result) {
+ case LibUsb.SUCCESS:
+ int trans = readBufTransferred.get();
+ byte[] receivedBytes = new byte[trans];
+ readBuffer.get(receivedBytes);
+ return receivedBytes;
+ case LibUsb.ERROR_TIMEOUT:
+ continue;
+ default:
+ logPrinter.print("TF Data transfer issue [read]\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ logPrinter.print("TF Execution stopped", EMsgType.FAIL);
+ return null;
+ }
+ }
+ logPrinter.print("TF Execution interrupted", EMsgType.INFO);
+ return null;
+ }
+
+ /**
+ * Status getter
+ * @return status
+ */
+ @Override
+ public EFileStatus getStatus() {
+ return status;
+ }
+}
diff --git a/src/main/java/nsusbloader/USB/UsbCommunications.java b/src/main/java/nsusbloader/USB/UsbCommunications.java
index a8033cc..97df6a6 100644
--- a/src/main/java/nsusbloader/USB/UsbCommunications.java
+++ b/src/main/java/nsusbloader/USB/UsbCommunications.java
@@ -1,41 +1,22 @@
package nsusbloader.USB;
-import javafx.application.Platform;
import javafx.concurrent.Task;
-import javafx.stage.FileChooser;
-import nsusbloader.MediatorControl;
import nsusbloader.ModelControllers.LogPrinter;
import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.NSLDataTypes.EMsgType;
-import nsusbloader.RainbowHexDump;
import org.usb4java.*;
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;
// TODO: add filter option to show only NSP files
public class UsbCommunications extends Task {
- private final int DEFAULT_INTERFACE = 0;
private LogPrinter logPrinter;
- private EFileStatus status = EFileStatus.FAILED;
-
private LinkedHashMap nspMap;
-
- private Context contextNS;
- private DeviceHandle handlerNS;
-
private String protocol;
-
private boolean nspFilterForGl;
- private boolean proxyForGL = false;
-
/*
Ok, here is a story. We will pass to NS only file names, not full path. => see nspMap where 'key' is a file name.
File name itself should not be greater then 512 bytes, but in real world it's limited by OS to something like 256 bytes.
@@ -58,1499 +39,38 @@ public class UsbCommunications extends Task {
@Override
protected Void call() {
- int result = -9999;
-
logPrinter.print("\tStart chain", EMsgType.INFO);
- // Creating Context required by libusb. Optional. TODO: Consider removing.
- contextNS = new Context();
- result = LibUsb.init(contextNS);
- if (result != LibUsb.SUCCESS) {
- logPrinter.print("libusb initialization\n Returned: "+result, EMsgType.FAIL);
- close();
+
+ UsbConnect usbConnect = new UsbConnect(logPrinter);
+
+ if (! usbConnect.isConnected()){
+ close(EFileStatus.FAILED);
return null;
}
+
+ DeviceHandle handler = usbConnect.getHandlerNS();
+
+ ITransferModule module;
+
+ if (protocol.equals("TinFoil"))
+ module = new TinFoil(handler, nspMap, this, logPrinter);
else
- logPrinter.print("libusb initialization", EMsgType.PASS);
+ module = new GoldLeaf(handler, nspMap, this, logPrinter, nspFilterForGl);
- // Searching for NS in devices: obtain list of all devices
- DeviceList deviceList = new DeviceList();
- result = LibUsb.getDeviceList(contextNS, deviceList);
- if (result < 0) {
- logPrinter.print("Get device list\n Returned: "+result, EMsgType.FAIL);
- close();
- return null;
- }
- else {
- logPrinter.print("Get device list", EMsgType.PASS);
- }
- // Searching for NS in devices: looking for NS
- DeviceDescriptor descriptor;
- Device deviceNS = null;
- for (Device device: deviceList){
- descriptor = new DeviceDescriptor(); // mmm.. leave it as is.
- result = LibUsb.getDeviceDescriptor(device, descriptor);
- if (result != LibUsb.SUCCESS){
- logPrinter.print("Read file descriptors for USB devices\n Returned: "+result, EMsgType.FAIL);
- LibUsb.freeDeviceList(deviceList, true);
- close();
- return null;
- }
- if ((descriptor.idVendor() == 0x057E) && descriptor.idProduct() == 0x3000){
- deviceNS = device;
- logPrinter.print("Read file descriptors for USB devices", EMsgType.PASS);
- break;
- }
- }
- // Free device list.
- if (deviceNS != null){
- logPrinter.print("NS in connected USB devices found", EMsgType.PASS);
- }
- else {
- logPrinter.print("NS in connected USB devices not found", EMsgType.FAIL);
- close();
- return null;
- }
- // Handle NS device
- handlerNS = new DeviceHandle();
- result = LibUsb.open(deviceNS, handlerNS);
- if (result != LibUsb.SUCCESS) {
- logPrinter.print("Open NS USB device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- if (result == LibUsb.ERROR_ACCESS)
- logPrinter.print("Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!\n\n" +
- "Steps to set 'udev' rules:\n" +
- "root # vim /etc/udev/rules.d/99-NS.rules\n" +
- "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"057e\", ATTRS{idProduct}==\"3000\", GROUP=\"plugdev\"\n" +
- "root # udevadm control --reload-rules && udevadm trigger\n", EMsgType.INFO);
- // Let's make a bit dirty workaround since such shit happened
- if (contextNS != null) {
- LibUsb.exit(contextNS);
- logPrinter.print("Requested context close", EMsgType.INFO);
- }
+ usbConnect.close();
- // Report status and close
- logPrinter.update(nspMap, status);
- logPrinter.print("\tEnd chain", EMsgType.INFO);
- logPrinter.close();
- return null;
- }
- else
- logPrinter.print("Open NS USB device", EMsgType.PASS);
+ close(module.getStatus());
- logPrinter.print("Free device list", EMsgType.INFO);
- LibUsb.freeDeviceList(deviceList, true);
-
- // DO some stuff to connected NS
- // Check if this device uses kernel driver and detach if possible:
- boolean canDetach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER); // if cant, it's windows ot old lib
- if (canDetach){
- int usedByKernel = LibUsb.kernelDriverActive(handlerNS, DEFAULT_INTERFACE);
- if (usedByKernel == LibUsb.SUCCESS){
- logPrinter.print("Can proceed with libusb driver", EMsgType.PASS); // we're good
- }
- else if (usedByKernel == 1) { // used by kernel
- result = LibUsb.detachKernelDriver(handlerNS, DEFAULT_INTERFACE);
- logPrinter.print("Detach kernel required", EMsgType.INFO);
- if (result != 0) {
- logPrinter.print("Detach kernel\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- close();
- return null;
- } else
- logPrinter.print("Detach kernel", EMsgType.PASS);
- }
- else
- logPrinter.print("Can't proceed with libusb driver\n Returned: "+UsbErrorCodes.getErrCode(usedByKernel), EMsgType.FAIL);
- }
- else
- logPrinter.print("libusb doesn't support function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. It's normal. Proceeding.", EMsgType.WARNING);
- /*
- // Reset device
- result = LibUsb.resetDevice(handlerNS);
- if (result == 0)
- logPrinter.print("Reset device", EMsgType.PASS);
- else {
- logPrinter.print("Reset device returned: " + result, EMsgType.FAIL);
- updateAndClose();
- return null;
- }
- */
- // Set configuration (soft reset if needed)
- result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need
- if (result != LibUsb.SUCCESS){
- logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- close();
- return null;
- }
- else {
- logPrinter.print("Set active configuration to device.", EMsgType.PASS);
- }
-
- // Claim interface
- result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE);
- if (result != LibUsb.SUCCESS) {
- logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- close();
- return null;
- }
- else
- logPrinter.print("Claim interface", EMsgType.PASS);
-
- //--------------------------------------------------------------------------------------------------------------
- if (protocol.equals("TinFoil")) {
- new TinFoil();
- } else {
- new GoldLeaf();
- }
-
- close();
return null;
}
- /**
- * Tinfoil processing
- * */
- private class TinFoil{
- TinFoil(){
- logPrinter.print("============= TinFoil =============", EMsgType.INFO);
-
- if (!sendListOfNSP())
- return;
-
- if (proceedCommands()) // REPORT SUCCESS
- status = EFileStatus.UPLOADED; // Don't change status that is already set to FAILED
- }
- /**
- * Send what NSP will be transferred
- * */
- private boolean sendListOfNSP(){
- // Send list of NSP files:
- // Proceed "TUL0"
- if (writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30}
- logPrinter.print("TF Send list of files: handshake", EMsgType.FAIL);
- return false;
- }
- else
- logPrinter.print("TF Send list of files: handshake", EMsgType.PASS);
- //Collect file names
- StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
- for(String nspFileName: nspMap.keySet()) {
- nspListNamesBuilder.append(nspFileName); // And here we come with java string default encoding (UTF-16)
- nspListNamesBuilder.append('\n');
- }
-
- byte[] nspListNames = nspListNamesBuilder.toString().getBytes(StandardCharsets.UTF_8);
- ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
- byteBuffer.putInt(nspListNames.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
- byte[] nspListSize = byteBuffer.array(); // TODO: rewind? not sure..
- //byteBuffer.reset();
-
- // Sending NSP list
- logPrinter.print("TF Send list of files", EMsgType.INFO);
- if (writeToUsb(nspListSize)) { // size of the list we're going to transfer goes...
- logPrinter.print(" [send list length]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [send list length]", EMsgType.PASS);
-
- if (writeToUsb(new byte[8])) { // 8 zero bytes goes...
- logPrinter.print(" [send padding]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [send padding]", EMsgType.PASS);
-
- if (writeToUsb(nspListNames)) { // list of the names goes...
- logPrinter.print(" [send list itself]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [send list itself]", EMsgType.PASS);
-
- return true;
- }
- /**
- * After we sent commands to NS, this chain starts
- * */
- private boolean proceedCommands(){
- logPrinter.print("TF Awaiting for NS commands.", EMsgType.INFO);
-
- /* byte[] magic = new byte[4];
- ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic);
- // Let's rephrase this 'string' */
- final byte[] magic = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // eq. 'TUC0' @ UTF-8 (actually ASCII lol, u know what I mean)
-
- byte[] receivedArray;
-
- while (true){
- if (isCancelled()) // Check if user interrupted process.
- return false;
- receivedArray = readFromUsb();
- if (receivedArray == null)
- return false; // catches exception
-
- if (!Arrays.equals(Arrays.copyOfRange(receivedArray, 0,4), magic)) // Bytes from 0 to 3 should contain 'magic' TUC0, so must be verified like this
- continue;
-
- // 8th to 12th(explicits) bytes in returned data stands for command ID as unsigned integer (Little-endian). Actually, we have to compare arrays here, but in real world it can't be greater then 0/1/2, thus:
- // BTW also protocol specifies 4th byte to be 0x00 kinda indicating that that this command is valid. But, as you may see, never happens other situation when it's not = 0.
- if (receivedArray[8] == 0x00){ //0x00 - exit
- logPrinter.print("TF Received EXIT command. Terminating.", EMsgType.PASS);
- return true; // All interaction with USB device should be ended (expected);
- }
- else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack).
- logPrinter.print("TF Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", EMsgType.PASS);
- /*// We can get in this pocket a length of file name (+32). Why +32? I dunno man.. Do we need this? Definitely not. This app can live without it.
- long receivedSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 12,20)).order(ByteOrder.LITTLE_ENDIAN).getLong();
- logsArea.appendText("[V] Received FILE_RANGE command. Size: "+Long.toUnsignedString(receivedSize)+"\n"); // this shit returns string that will be chosen next '+32'. And, BTW, can't be greater then 512
- */
- if (!fileRangeCmd()) {
- return false; // catches exception
- }
- }
- }
- }
- /**
- * This is what returns requested file (files)
- * Executes multiple times
- * @return 'true' if everything is ok
- * 'false' is error/exception occurs
- * */
- private boolean fileRangeCmd(){
- boolean isProgessBarInitiated = false;
-
- byte[] receivedArray;
- // Here we take information of what other side wants
- receivedArray = readFromUsb();
- if (receivedArray == null)
- return false;
-
- // range_offset of the requested file. In the begining it will be 0x10.
- long receivedRangeSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 0,8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
- byte[] receivedRangeSizeRAW = Arrays.copyOfRange(receivedArray, 0,8); // used (only) when we use sendResponse(). It's just simply.
- long receivedRangeOffset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
- /* Below, it's REAL NSP file name length that we sent before among others (WITHOUT +32 byes). It can't be greater then... see what is written in the beginning of this code.
- We don't need this since in next pocket we'll get name itself UTF-8 encoded. Could be used to double-checks or something like that.
- long receivedNspNameLen = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 16,24)).order(ByteOrder.LITTLE_ENDIAN).getLong(); */
-
- // Requesting UTF-8 file name required:
- receivedArray = readFromUsb();
- if (receivedArray == null)
- return false;
-
- String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8);
- logPrinter.print("TF Reply to requested file: "+receivedRequestedNSP
- +"\n Range Size: "+receivedRangeSize
- +"\n Range Offset: "+receivedRangeOffset, EMsgType.INFO);
-
- // Sending response header
- if (!sendResponse(receivedRangeSizeRAW)) // Get receivedRangeSize in 'RAW' format exactly as it has been received. It's simply.
- return false;
-
- try {
-
- BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
- byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
-
- if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
- logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
- return false;
- }
-
- long currentOffset = 0;
- // 'End Offset' equal to receivedRangeSize.
- int readPice = 8388608; // = 8Mb
-
- while (currentOffset < receivedRangeSize){
- if (isCancelled()) // Check if user interrupted process.
- return true;
- if ((currentOffset + readPice) >= receivedRangeSize )
- readPice = Math.toIntExact(receivedRangeSize - currentOffset);
- //System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // TODO: NOTE: DEBUG
- // updating progress bar (if a lot of data requested) START BLOCK
- //-----------------------------------------/
- try {
- logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
- }catch (InterruptedException ie){
- getException().printStackTrace(); // TODO: Do something with this
- }
- //-----------------------------------------/
- bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
-
- if (bufferedInStream.read(bufferCurrent) != readPice) { // changed since @ v0.3.2
- logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
- return false;
- }
- //write to USB
- if (writeToUsb(bufferCurrent)) {
- logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
- return false;
- }
- currentOffset += readPice;
- }
- bufferedInStream.close();
- //-----------------------------------------/
- try{
- logPrinter.updateProgress(1.0);
- }
- catch (InterruptedException ie){
- getException().printStackTrace(); // TODO: Do something with this
- }
- //-----------------------------------------/
- } catch (FileNotFoundException fnfe){
- logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL);
- fnfe.printStackTrace();
- return false;
- } catch (IOException ioe){
- logPrinter.print("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL);
- ioe.printStackTrace();
- return false;
- } catch (ArithmeticException ae){
- logPrinter.print("TF ArithmeticException (can't cast 'offset end' - 'offsets current' to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL);
- ae.printStackTrace();
- return false;
- }
-
- return true;
- }
- /**
- * Send response header.
- * @return true if everything OK
- * false if failed
- * */
- private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
- logPrinter.print("TF Sending response", EMsgType.INFO);
- if (writeToUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0'
- (byte) 0x01, // CMD_TYPE_RESPONSE = 1
- (byte) 0x00, (byte) 0x00, (byte) 0x00, // kinda padding. Guys, didn't you want to use integer value for CMD semantic?
- (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00} ) // Send integer value of '1' in Little-endian format.
- ){
- logPrinter.print(" [1/3]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [1/3]", EMsgType.PASS);
- if(writeToUsb(rangeSize)) { // Send EXACTLY what has been received
- logPrinter.print(" [2/3]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [2/3]", EMsgType.PASS);
- if(writeToUsb(new byte[12])) { // kinda another one padding
- logPrinter.print(" [3/3]", EMsgType.FAIL);
- return false;
- }
- logPrinter.print(" [3/3]", EMsgType.PASS);
- return true;
- }
-
- }
/**
- * GoldLeaf processing
- * */
- private class GoldLeaf{
- // 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, 0x64, (byte) 0xcb, 0x00, 0x00}; // used @ writeToUsb_GLCMD
-
- // System.out.println((356 & 0x1FF) | ((1 + 100) & 0x1FFF) << 9); // 52068 // 0x00 0x00 0xCB 0x64
- private final byte[] GL_OBJ_TYPE_FILE = new byte[]{0x01, 0x00, 0x00, 0x00};
- private final byte[] GL_OBJ_TYPE_DIR = new byte[]{0x02, 0x00, 0x00, 0x00};
-
- private String recentPath = null;
- private String[] recentDirs = null;
- private String[] recentFiles = null;
-
- private String[] nspMapKeySetIndexes;
-
- private String openReadFileNameAndPath;
- private RandomAccessFile randAccessFile;
-
- private HashMap writeFilesMap;
-
- private boolean isWindows;
- private String homePath;
- // For using in CMD_SelectFile with SPEC:/ prefix
- private File selectedFile;
-
- GoldLeaf(){
- final byte CMD_GetDriveCount = 0x00;
- final byte CMD_GetDriveInfo = 0x01;
- final byte CMD_StatPath = 0x02; // TODO: proxy done [proxy: in case if folder contains ENG+RUS+UKR file names works incorrect]
- final byte CMD_GetFileCount = 0x03;
- final byte CMD_GetFile = 0x04; // TODO: proxy done
- final byte CMD_GetDirectoryCount = 0x05;
- final byte CMD_GetDirectory = 0x06; // TODO: proxy done
- final byte CMD_ReadFile = 0x07; // TODO: no way to do poxy
- final byte CMD_WriteFile = 0x08; // TODO: add predictable behavior
- final byte CMD_Create = 0x09;
- final byte CMD_Delete = 0x0a;//10
- final byte CMD_Rename = 0x0b;//11
- final byte CMD_GetSpecialPathCount = 0x0c;//12 // Special folders count; simplified usage @ NS-UL
- final byte CMD_GetSpecialPath = 0x0d;//13 // Information about special folders; simplified usage @ NS-UL
- final byte CMD_SelectFile = 0x0e;//14
- final byte CMD_Max = 0x0f;//15 // not used @ NS-UL & GT
-
- final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49};
-
- logPrinter.print("============= GoldLeaf =============\n\tVIRT:/ equals files added into the application\n\tHOME:/ equals "
- +System.getProperty("user.home"), EMsgType.INFO);
- // Let's collect file names to the array to simplify our life
- writeFilesMap = new HashMap<>();
- int i = 0;
- nspMapKeySetIndexes = new String[nspMap.size()];
- for (String fileName : nspMap.keySet())
- nspMapKeySetIndexes[i++] = fileName;
-
- status = EFileStatus.UNKNOWN;
-
- isWindows = System.getProperty("os.name").contains("Windows");
-
- homePath = System.getProperty("user.home")+File.separator;
-
- // Go parse commands
- byte[] readByte;
- int someLength1,
- someLength2;
- while (! isCancelled()) { // Till user interrupted process.
- readByte = readGL();
-
- if (readByte == null) // Issue @ readFromUsbGL method
- return;
- else if(readByte.length < 4096) { // Just timeout of waiting for reply; continue loop
- closeOpenedReadFilesGl();
- continue;
- }
-
- //RainbowHexDump.hexDumpUTF16LE(readByte); // DEBUG
- //System.out.println("CHOICE: "+readByte[4]); // DEBUG
-
- if (Arrays.equals(Arrays.copyOfRange(readByte, 0,4), CMD_GLCI)) {
- switch (readByte[4]) {
- case CMD_GetDriveCount:
- if (getDriveCount())
- return;
- break;
- case CMD_GetDriveInfo:
- if (getDriveInfo(arrToIntLE(readByte,8)))
- return;
- break;
- case CMD_GetSpecialPathCount:
- if (getSpecialPathCount())
- return;
- break;
- case CMD_GetSpecialPath:
- if (getSpecialPath(arrToIntLE(readByte,8)))
- return;
- break;
- case CMD_GetDirectoryCount:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), true))
- return;
- break;
- case CMD_GetFileCount:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (getDirectoryOrFileCount(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), false))
- return;
- break;
- case CMD_GetDirectory:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (getDirectory(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), arrToIntLE(readByte, someLength1+12)))
- return;
- break;
- case CMD_GetFile:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (getFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE), arrToIntLE(readByte, someLength1+12)))
- return;
- break;
- case CMD_StatPath:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (statPath(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE)))
- return;
- break;
- case CMD_Rename:
- someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
- someLength2 = arrToIntLE(readByte, 16+someLength1) * 2; // Since GL 0.7
- if (rename(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE),
- new String(readByte, 16+someLength1+4, someLength2, StandardCharsets.UTF_16LE)))
- return;
- break;
- case CMD_Delete:
- someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
- if (delete(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE)))
- return;
- break;
- case CMD_Create:
- someLength1 = arrToIntLE(readByte, 12) * 2; // Since GL 0.7
- if (create(new String(readByte, 16, someLength1, StandardCharsets.UTF_16LE), readByte[8]))
- return;
- break;
- case CMD_ReadFile:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (readFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE),
- arrToLongLE(readByte, 12+someLength1),
- arrToLongLE(readByte, 12+someLength1+8)))
- return;
- break;
- case CMD_WriteFile:
- someLength1 = arrToIntLE(readByte, 8) * 2; // Since GL 0.7
- if (writeFile(new String(readByte, 12, someLength1, StandardCharsets.UTF_16LE),
- arrToLongLE(readByte, 12+someLength1)))
- return;
- break;
- case CMD_SelectFile:
- if (selectFile())
- return;
- break;
- default:
- writeGL_FAIL("GL Unknown command: "+readByte[4]+" [it's a very bad sign]");
- }
- }
- }
- // Close (and flush) all opened streams.
- if (writeFilesMap.size() != 0){
- for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
- try{
- fBufOutStream.close();
- }catch (IOException ignored){}
- }
- }
- closeOpenedReadFilesGl();
- }
-
- /**
- * Close files opened for read/write
- */
- private void closeOpenedReadFilesGl(){
- if (openReadFileNameAndPath != null){ // Perfect time to close our opened files
- try{
- randAccessFile.close();
- }
- catch (IOException ignored){}
- openReadFileNameAndPath = null;
- randAccessFile = null;
- }
- }
- /**
- * Handle GetDriveCount
- * @return true if failed
- * false if everything is ok
- */
- private boolean getDriveCount(){
- // Let's declare 2 drives
- byte[] drivesCnt = intToArrLE(2); //2
- // Write count of drives
- if (writeGL_PASS(drivesCnt)) {
- logPrinter.print("GL Handle 'ListDrives' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- /**
- * Handle GetDriveInfo
- * @return true if failed
- * false if everything is ok
- */
- private boolean getDriveInfo(int driveNo){
- if (driveNo < 0 || driveNo > 1){
- return writeGL_FAIL("GL Handle 'GetDriveInfo' command [no such drive]");
- }
-
- byte[] driveLabel,
- driveLabelLen,
- driveLetter,
- driveLetterLen,
- totalFreeSpace,
- totalSize;
- long totalSizeLong = 0;
-
- // 0 == VIRTUAL DRIVE
- if (driveNo == 0){
- driveLabel = "Virtual".getBytes(StandardCharsets.UTF_16LE);
- driveLabelLen = intToArrLE(driveLabel.length / 2); // since GL 0.7
- driveLetter = "VIRT".getBytes(StandardCharsets.UTF_16LE); // TODO: Consider moving to class field declaration
- driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
- totalFreeSpace = new byte[4];
- for (File nspFile : nspMap.values()){
- totalSizeLong += nspFile.length();
- }
- totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
- }
- else { //1 == User home dir
- driveLabel = "Home".getBytes(StandardCharsets.UTF_16LE);
- driveLabelLen = intToArrLE(driveLabel.length / 2);// since GL 0.7
- driveLetter = "HOME".getBytes(StandardCharsets.UTF_16LE);
- driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
- File userHomeDir = new File(System.getProperty("user.home"));
- long totalFreeSpaceLong = userHomeDir.getFreeSpace();
- totalFreeSpace = Arrays.copyOfRange(longToArrLE(totalFreeSpaceLong), 0, 4); // Dirty hack; now for GL!;
- totalSizeLong = userHomeDir.getTotalSpace();
- totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
-
- //System.out.println("totalSize: "+totalSizeLong+"totalFreeSpace: "+totalFreeSpaceLong);
- }
-
- 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)) {
- logPrinter.print("GL Handle 'GetDriveInfo' command", EMsgType.FAIL);
- return true;
- }
-
- return false;
- }
- /**
- * Handle SpecialPathCount
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getSpecialPathCount(){
- // Let's declare nothing =)
- byte[] specialPathCnt = intToArrLE(0);
- // Write count of special paths
- if (writeGL_PASS(specialPathCnt)) {
- logPrinter.print("GL Handle 'SpecialPathCount' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- /**
- * Handle SpecialPath
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getSpecialPath(int specialPathNo){
- return writeGL_FAIL("GL Handle 'SpecialPath' command [not supported]");
- }
- /**
- * Handle GetDirectoryCount & GetFileCount
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getDirectoryOrFileCount(String path, boolean isGetDirectoryCount) {
- if (path.equals("VIRT:/")) {
- if (isGetDirectoryCount){
- if (writeGL_PASS()) {
- logPrinter.print("GL Handle 'GetDirectoryCount' command", EMsgType.FAIL);
- return true;
- }
- }
- else {
- if (writeGL_PASS(intToArrLE(nspMap.size()))) {
- logPrinter.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()) {
- logPrinter.print("GL Handle 'GetDirectoryOrFileCount' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- // Sorting is mandatory TODO: 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))) {
- logPrinter.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()) {
- logPrinter.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))) {
- logPrinter.print("GL Handle 'GetFileCount' command Count = 1", EMsgType.FAIL);
- return true;
- }
- }
- else
- return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] (file) - "+path);
- }
- else { // If requested drive is not VIRT and not HOME then reply error
- return writeGL_FAIL("GL Handle 'GetDirectoryOrFileCount' command [unknown drive request] "+(isGetDirectoryCount?"(dir) - ":"(file) - ")+path);
- }
- return false;
- }
- /**
- * Handle GetDirectory
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getDirectory(String dirName, int subDirNo){
- if (dirName.startsWith("HOME:/")) {
- dirName = updateHomePath(dirName);
-
- List command = new LinkedList<>();
-
- if (dirName.equals(recentPath) && recentDirs != null && recentDirs.length != 0){
- byte[] dirNameBytes = recentDirs[subDirNo].getBytes(StandardCharsets.UTF_16LE);
-
- command.add(intToArrLE(dirNameBytes.length / 2)); // Since GL 0.7
- 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_16LE);
- command.add(intToArrLE(dirBytesName.length / 2)); // Since GL 0.7
- command.add(dirBytesName);
- }
- else
- return writeGL_FAIL("GL Handle 'GetDirectory' command [doesn't exist or not a folder]");
- }
- //if (proxyForGL) // TODO: NOTE: PROXY TAILS
- // return proxyGetDirFile(true);
-
- if (writeGL_PASS(command)) {
- logPrinter.print("GL Handle 'GetDirectory' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- // VIRT:// and any other
- return writeGL_FAIL("GL Handle 'GetDirectory' command for virtual drive [no folders support]");
- }
- /**
- * Handle GetFile
- * @return true if failed
- * false if everything is ok
- * */
- private boolean getFile(String dirName, int subDirNo){
- List command = new LinkedList<>();
-
- if (dirName.startsWith("HOME:/")) {
- dirName = updateHomePath(dirName);
-
- if (dirName.equals(recentPath) && recentFiles != null && recentFiles.length != 0){
- byte[] fileNameBytes = recentFiles[subDirNo].getBytes(StandardCharsets.UTF_16LE);
-
- command.add(intToArrLE(fileNameBytes.length / 2)); //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_16LE);
- command.add(intToArrLE(fileNameBytes.length / 2)); //Since GL 0.7
- command.add(fileNameBytes);
- }
- else
- return writeGL_FAIL("GL Handle 'GetFile' command [doesn't exist or not a folder]");
- }
- //if (proxyForGL) // TODO: NOTE: PROXY TAILS
- // return proxyGetDirFile(false);
- if (writeGL_PASS(command)) {
- logPrinter.print("GL Handle 'GetFile' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- else if (dirName.equals("VIRT:/")){
- if (nspMap.size() != 0){ // therefore nspMapKeySetIndexes also != 0
- byte[] fileNameBytes = nspMapKeySetIndexes[subDirNo].getBytes(StandardCharsets.UTF_16LE);
- command.add(intToArrLE(fileNameBytes.length / 2)); // since GL 0.7
- command.add(fileNameBytes);
- if (writeGL_PASS(command)) {
- logPrinter.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_16LE);
- command.add(intToArrLE(fileNameBytes.length / 2)); // since GL 0.7
- command.add(fileNameBytes);
- if (writeGL_PASS(command)) {
- logPrinter.print("GL Handle 'GetFile' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- // any other cases
- return writeGL_FAIL("GL Handle 'GetFile' command for virtual drive [no folders support?]");
- }
- /**
- * Handle StatPath
- * @return true if failed
- * false if everything is ok
- * */
- private boolean statPath(String filePath){
- List command = new LinkedList<>();
-
- if (filePath.startsWith("HOME:/")){
- filePath = updateHomePath(filePath);
- //if (proxyForGL) // TODO:NOTE PROXY TAILS
- // return proxyStatPath(filePath); // dirty name
-
- File fileDirElement = new File(filePath);
- if (fileDirElement.exists()){
- if (fileDirElement.isDirectory())
- command.add(GL_OBJ_TYPE_DIR);
- else {
- command.add(GL_OBJ_TYPE_FILE);
- command.add(longToArrLE(fileDirElement.length()));
- }
- if (writeGL_PASS(command)) {
- logPrinter.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
- command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
- if (writeGL_PASS(command)) {
- logPrinter.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)) {
- logPrinter.print("GL Handle 'StatPath' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- return writeGL_FAIL("GL Handle 'StatPath' command [no such folder] - "+filePath);
- }
- /**
- * Handle 'Rename' that is actually 'mv'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean rename(String fileName, String newFileName){
- if (fileName.startsWith("HOME:/")){
- // This shit takes too much time to explain, but such behaviour won't let GL to fail
- this.recentPath = null;
- this.recentFiles = null;
- this.recentDirs = null;
- fileName = updateHomePath(fileName);
- newFileName = updateHomePath(newFileName);
-
- File currentFile = new File(fileName);
- File newFile = new File(newFileName);
- if (! newFile.exists()){ // Else, report error
- try {
- if (currentFile.renameTo(newFile)){
- if (writeGL_PASS()) {
- logPrinter.print("GL Handle 'Rename' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- catch (SecurityException ignored){} // Ah, leave it
- }
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Rename' command [not supported for virtual drive/wrong drive/file with such name already exists/read-only directory]");
- }
- /**
- * Handle 'Delete'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean delete(String fileName) {
- if (fileName.startsWith("HOME:/")) {
- fileName = updateHomePath(fileName);
-
- File fileToDel = new File(fileName);
- try {
- if (fileToDel.delete()){
- if (writeGL_PASS()) {
- logPrinter.print("GL Handle 'Rename' command.", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
- catch (SecurityException ignored){} // Ah, leave it
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
- }
- /**
- * Handle 'Create'
- * @param type 1 for file
- * 2 for folder
- * @param fileName full path including new file name in the end
- * @return true if failed
- * false if everything is ok
- * */
- private boolean create(String fileName, byte type) {
- if (fileName.startsWith("HOME:/")) {
- fileName = updateHomePath(fileName);
- File fileToCreate = new File(fileName);
- boolean result = false;
- if (type == 1){
- try {
- result = fileToCreate.createNewFile();
- }
- catch (SecurityException | IOException ignored){}
- }
- else if (type == 2){
- try {
- result = fileToCreate.mkdir();
- }
- catch (SecurityException ignored){}
- }
- if (result){
- if (writeGL_PASS()) {
- logPrinter.print("GL Handle 'Create' command.", EMsgType.FAIL);
- return true;
- }
- //logPrinter.print("GL Handle 'Create' command.", EMsgType.PASS);
- return false;
- }
- }
- // For VIRT:/ and others we don't serve requests
- return writeGL_FAIL("GL Handle 'Delete' command [not supported for virtual drive/wrong drive/read-only directory]");
- }
-
- /**
- * Handle 'ReadFile'
- * @param fileName full path including new file name in the end
- * @param offset requested offset
- * @param size requested size
- * @return true if failed
- * false if everything is ok
- * */
- private boolean readFile(String fileName, long offset, long size) {
- //System.out.println("readFile "+fileName+" "+offset+" "+size);
- 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 (IOException ignored){}
- }
- // Open what has to be opened
- try{
- randAccessFile = new RandomAccessFile(nspMap.get(fileName.substring(6)), "r");
- openReadFileNameAndPath = fNamePath;
- }
- catch (IOException 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 ignored){}
- }
- // Open what has to be opened
- try{
- randAccessFile = new RandomAccessFile(fileName, "r");
- openReadFileNameAndPath = fileName;
- }catch (IOException ioe){
- return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
- }
- }
- }
- //----------------------- Actual transfer chain ------------------------
- try{
- randAccessFile.seek(offset);
- byte[] chunk = new byte[(int)size]; // WTF MAN?
- // Let's find out how much bytes we got
- int bytesRead = randAccessFile.read(chunk);
- // Let's check that we read expected size
- if (bytesRead != (int)size)
- return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
- // Let's tell as a command about our result.
- if (writeGL_PASS(longToArrLE(size))) {
- logPrinter.print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
- return true;
- }
- // Let's bypass bytes we read total
- if (writeToUsb(chunk)) {
- logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- catch (IOException ioe){
- try{
- randAccessFile.close();
- }
- catch (IOException ioee){
- logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioee.getMessage(), EMsgType.WARNING);
- }
- openReadFileNameAndPath = null;
- randAccessFile = 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
- * @param size requested size
- * @return true if failed
- * false if everything is ok
- * */
- private boolean writeFile(String fileName, long size) {
- //System.out.println("writeFile "+fileName+" "+size);
- if (fileName.startsWith("VIRT:/")){
- return writeGL_FAIL("GL Handle 'WriteFile' command [not supported for virtual drive]");
- }
- else {
- fileName = updateHomePath(fileName);
- // Check if we didn't see this (or any) file during this session
- if (writeFilesMap.size() == 0 || (! writeFilesMap.containsKey(fileName))){
- // Open what we have to open
- File writeFile = new File(fileName);
- // If this file exists GL will take care
- // Otherwise, let's add it
- try{
- BufferedOutputStream writeFileBufOutStream = new BufferedOutputStream(new FileOutputStream(writeFile, true));
- writeFilesMap.put(fileName, writeFileBufOutStream);
- } catch (IOException ioe){
- return writeGL_FAIL("GL Handle 'WriteFile' command [IOException]\n\t"+ioe.getMessage());
- }
- }
- // Now we have stream
- BufferedOutputStream myStream = writeFilesMap.get(fileName);
-
- byte[] transferredData;
-
- if ((transferredData = readGL_file()) == null){
- logPrinter.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()) {
- logPrinter.print("GL Handle 'WriteFile' command", EMsgType.FAIL);
- return true;
- }
- return false;
- }
- }
-
- /**
- * Handle 'SelectFile'
- * @return true if failed
- * false if everything is ok
- * */
- private boolean selectFile(){
- File selectedFile = CompletableFuture.supplyAsync(() -> {
- FileChooser fChooser = new FileChooser();
- fChooser.setTitle(MediatorControl.getInstance().getContoller().getResourceBundle().getString("btn_OpenFile")); // TODO: FIX BAD IMPLEMENTATION
- fChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: Consider fixing; not a prio.
- fChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("*", "*"));
- return fChooser.showOpenDialog(null); // Leave as is for now.
- }, Platform::runLater).join();
-
- if (selectedFile != null){
- List command = new LinkedList<>();
- byte[] selectedFileNameBytes = ("SPEC:/"+selectedFile.getName()).getBytes(StandardCharsets.UTF_16LE);
- command.add(intToArrLE(selectedFileNameBytes.length / 2)); // since GL 0.7
- command.add(selectedFileNameBytes);
- if (writeGL_PASS(command)) {
- logPrinter.print("GL Handle 'SelectFile' command", EMsgType.FAIL);
- this.selectedFile = null;
- return true;
- }
- this.selectedFile = selectedFile;
- return false;
- }
- // Nothing selected; Report failure.
- this.selectedFile = null;
- return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
- }
-
- /*----------------------------------------------------*/
- /* GL READ/WRITE USB SPECIFIC */
- /*----------------------------------------------------*/
-
- /**
- * Write new command. Shitty implementation.
- * */
- private boolean writeGL_PASS(byte[] message){
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(message);
- return writeToUsb(writeBuffer.array());
- }
- private boolean writeGL_PASS(){
- return writeToUsb(Arrays.copyOf(CMD_GLCO_SUCCESS, 4096));
- }
- private boolean writeGL_PASS(List messages){
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- writeBuffer.put(CMD_GLCO_SUCCESS);
- for (byte[] arr : messages)
- writeBuffer.put(arr);
- return writeToUsb(writeBuffer.array());
- }
-
- private boolean writeGL_FAIL(String reportToUImsg){
- if (writeToUsb(Arrays.copyOf(CMD_GLCO_FAILURE, 4096))){
- logPrinter.print(reportToUImsg, EMsgType.WARNING);
- return true;
- }
- logPrinter.print(reportToUImsg, EMsgType.FAIL);
- return false;
- }
- private byte[] readGL(){
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(4096); // GL really?
- // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
- IntBuffer readBufTransferred = IntBuffer.allocate(1);
-
- int result;
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
-
- if (result != LibUsb.SUCCESS && result != LibUsb.ERROR_TIMEOUT){
- logPrinter.print("GL Data transfer (read) issue\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- logPrinter.print("Execution stopped", EMsgType.FAIL);
- return null;
- }
- else {
- int trans = readBufTransferred.get();
- byte[] receivedBytes = new byte[trans];
- readBuffer.get(receivedBytes);
- return receivedBytes;
- }
- }
- private byte[] readGL_file(){
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(8388608); // Just don't ask..
- // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
- IntBuffer readBufTransferred = IntBuffer.allocate(1); // Works for 8mb
-
- int result;
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 5000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 TODO: Fix or leave as is
-
- if (result != LibUsb.SUCCESS && result != LibUsb.ERROR_TIMEOUT){
- logPrinter.print("GL Data transfer (read) issue\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- logPrinter.print("Execution stopped", EMsgType.FAIL);
- return null;
- }
- else {
- int trans = readBufTransferred.get();
- byte[] receivedBytes = new byte[trans];
- readBuffer.get(receivedBytes);
- return receivedBytes;
- }
- }
- /*----------------------------------------------------*/
- /* GL HELPERS */
- /*----------------------------------------------------*/
- /**
- * Convert path received from GL to normal
- */
- private String updateHomePath(String glPath){
- if (isWindows)
- glPath = glPath.replaceAll("/", "\\\\");
- glPath = homePath+glPath.substring(6); // Do not use replaceAll since it will consider \ as special directive
- return glPath;
- }
- /**
- * Convert INT (Little endian) value to bytes-array representation
- * */
- private byte[] intToArrLE(int value){
- ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.putInt(value);
- return byteBuffer.array();
- }
- /**
- * Convert LONG (Little endian) value to bytes-array representation
- * */
- private byte[] longToArrLE(long value){
- ByteBuffer byteBuffer = ByteBuffer.allocate(Long.BYTES).order(ByteOrder.LITTLE_ENDIAN);
- byteBuffer.putLong(value);
- return byteBuffer.array();
- }
- /**
- * Convert bytes-array to INT value (Little endian)
- * */
- private int arrToIntLE(byte[] byteArrayWithInt, int intStartPosition){
- return ByteBuffer.wrap(byteArrayWithInt).order(ByteOrder.LITTLE_ENDIAN).getInt(intStartPosition);
- }
- /**
- * Convert bytes-array to LONG value (Little endian)
- * */
- private long arrToLongLE(byte[] byteArrayWithLong, int intStartPosition){
- return ByteBuffer.wrap(byteArrayWithLong).order(ByteOrder.LITTLE_ENDIAN).getLong(intStartPosition);
- }
-
- /*----------------------------------------------------*/
- /* GL EXPERIMENTAL PART */
- /* (left for better times) */
- /*----------------------------------------------------*/
- /*
- private boolean proxyStatPath(String path) {
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- List fileBytesSize = new LinkedList<>();
- if ((recentDirs.length == 0) && (recentFiles.length == 0)) {
- return writeGL_FAIL("proxyStatPath");
- }
- if (recentDirs.length > 0){
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(GL_OBJ_TYPE_DIR);
- byte[] resultingDir = writeBuffer.array();
- writeToUsb(resultingDir);
- for (int i = 1; i < recentDirs.length; i++) {
- readGL();
- writeToUsb(resultingDir);
- }
- }
- if (recentFiles.length > 0){
- path = path.replaceAll(recentDirs[0]+"$", ""); // Remove the name from path
- for (String fileName : recentFiles){
- File f = new File(path+fileName);
- fileBytesSize.add(longToArrLE(f.length()));
- }
- writeBuffer.clear();
- for (int i = 0; i < recentFiles.length; i++){
- readGL();
- writeBuffer.clear();
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(GL_OBJ_TYPE_FILE);
- writeBuffer.put(fileBytesSize.get(i));
- writeToUsb(writeBuffer.array());
- }
- }
- return false;
- }
-
- private boolean proxyGetDirFile(boolean forDirs){
- ByteBuffer writeBuffer = ByteBuffer.allocate(4096);
- List dirBytesNameSize = new LinkedList<>();
- List dirBytesName = new LinkedList<>();
- if (forDirs) {
- if (recentDirs.length <= 0)
- return writeGL_FAIL("proxyGetDirFile");
- for (String dirName : recentDirs) {
- byte[] name = dirName.getBytes(StandardCharsets.UTF_16LE);
- dirBytesNameSize.add(intToArrLE(name.length));
- dirBytesName.add(name);
- }
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(dirBytesNameSize.get(0));
- writeBuffer.put(dirBytesName.get(0));
- writeToUsb(writeBuffer.array());
- writeBuffer.clear();
- for (int i = 1; i < recentDirs.length; i++){
- readGL();
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(dirBytesNameSize.get(i));
- writeBuffer.put(dirBytesName.get(i));
- writeToUsb(writeBuffer.array());
- writeBuffer.clear();
- }
- }
- else {
- if (recentDirs.length <= 0)
- return writeGL_FAIL("proxyGetDirFile");
- for (String dirName : recentFiles){
- byte[] name = dirName.getBytes(StandardCharsets.UTF_16LE);
- dirBytesNameSize.add(intToArrLE(name.length));
- dirBytesName.add(name);
- }
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(dirBytesNameSize.get(0));
- writeBuffer.put(dirBytesName.get(0));
- writeToUsb(writeBuffer.array());
- writeBuffer.clear();
- for (int i = 1; i < recentFiles.length; i++){
- readGL();
- writeBuffer.put(CMD_GLCO_SUCCESS);
- writeBuffer.put(dirBytesNameSize.get(i));
- writeBuffer.put(dirBytesName.get(i));
- writeToUsb(writeBuffer.array());
- writeBuffer.clear();
- }
- }
- return false;
- }
- */
- }
- //------------------------------------------------------------------------------------------------------------------
- /**
- * Correct exit
- * */
- private void close(){
- // Close handler in the end
- if (handlerNS != null) {
- // Try to release interface
- int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
-
- if (result != LibUsb.SUCCESS)
- logPrinter.print("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING);
- else
- logPrinter.print("Release interface", EMsgType.PASS);
-
- LibUsb.close(handlerNS);
- logPrinter.print("Requested handler close", EMsgType.INFO);
- }
- // Close context in the end
- if (contextNS != null) {
- LibUsb.exit(contextNS);
- logPrinter.print("Requested context close", EMsgType.INFO);
- }
-
- // Report status and close
+ * Report status and close
+ */
+ private void close(EFileStatus status){
logPrinter.update(nspMap, status);
logPrinter.print("\tEnd chain", EMsgType.INFO);
logPrinter.close();
}
- /**
- * Sending any byte array to USB device
- * @return 'false' if no issues
- * 'true' if errors happened
- * */
- private boolean writeToUsb(byte[] message){
- ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
- writeBuffer.put(message);// DONT EVEN THINK OF USING writeBuffer.rewind(); // well..
- //RainbowHexDump.hexDumpUTF16LE(message);
- //System.out.println("-------------------");
- IntBuffer writeBufTransferred = IntBuffer.allocate(1);
- int result;
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
- if (result != LibUsb.SUCCESS){
- logPrinter.print("Data transfer (write) issue\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- logPrinter.print("Execution stopped", EMsgType.FAIL);
- return true;
- }
- else {
- if (writeBufTransferred.get() != message.length){
- logPrinter.print("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
- return true;
- }
- else {
- return false;
- }
- }
- }
- /**
- * Reading what USB device responded.
- * @return byte array if data read successful
- * 'null' if read failed
- * */
- private byte[] readFromUsb(){
- ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);
- // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
- IntBuffer readBufTransferred = IntBuffer.allocate(1);
- int result;
- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
-
- if (result != LibUsb.SUCCESS){
- logPrinter.print("Data transfer (read) issue\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
- logPrinter.print("Execution stopped", EMsgType.FAIL);
- return null;
- } else {
- int trans = readBufTransferred.get();
- byte[] receivedBytes = new byte[trans];
- readBuffer.get(receivedBytes);
- /* DEBUG START----------------------------------------------------------------------------------------------*
- hexDumpUTF8(receivedBytes);
- // DEBUG END----------------------------------------------------------------------------------------------*/
- return receivedBytes;
- }
- }
}
\ No newline at end of file
diff --git a/src/main/java/nsusbloader/USB/UsbConnect.java b/src/main/java/nsusbloader/USB/UsbConnect.java
new file mode 100644
index 0000000..2e95cf0
--- /dev/null
+++ b/src/main/java/nsusbloader/USB/UsbConnect.java
@@ -0,0 +1,183 @@
+package nsusbloader.USB;
+
+import nsusbloader.ModelControllers.LogPrinter;
+import nsusbloader.NSLDataTypes.EMsgType;
+import org.usb4java.*;
+
+class UsbConnect {
+ private final int DEFAULT_INTERFACE = 0;
+
+ private Context contextNS;
+ private DeviceHandle handlerNS;
+
+ private LogPrinter logPrinter;
+
+ private boolean connected;
+
+ UsbConnect(LogPrinter logPrinter){
+ this.logPrinter = logPrinter;
+ this.connected = false;
+
+ int result;
+
+ // Creating Context required by libusb. Optional. TODO: Consider removing.
+ contextNS = new Context();
+ result = LibUsb.init(contextNS);
+ if (result != LibUsb.SUCCESS) {
+ logPrinter.print("libusb initialization\n Returned: "+result, EMsgType.FAIL);
+ close();
+ return;
+ }
+ else
+ logPrinter.print("libusb initialization", EMsgType.PASS);
+
+ // Searching for NS in devices: obtain list of all devices
+ DeviceList deviceList = new DeviceList();
+ result = LibUsb.getDeviceList(contextNS, deviceList);
+ if (result < 0) {
+ logPrinter.print("Get device list\n Returned: "+result, EMsgType.FAIL);
+ close();
+ return;
+ }
+ else
+ logPrinter.print("Get device list", EMsgType.PASS);
+ // Searching for NS in devices: looking for NS
+ DeviceDescriptor descriptor;
+ Device deviceNS = null;
+ for (Device device: deviceList){
+ descriptor = new DeviceDescriptor(); // mmm.. leave it as is.
+ result = LibUsb.getDeviceDescriptor(device, descriptor);
+ if (result != LibUsb.SUCCESS){
+ logPrinter.print("Read file descriptors for USB devices\n Returned: "+result, EMsgType.FAIL);
+ LibUsb.freeDeviceList(deviceList, true);
+ close();
+ return;
+ }
+ if ((descriptor.idVendor() == 0x057E) && descriptor.idProduct() == 0x3000){
+ deviceNS = device;
+ logPrinter.print("Read file descriptors for USB devices", EMsgType.PASS);
+ break;
+ }
+ }
+ // Free device list.
+ if (deviceNS != null){
+ logPrinter.print("NS in connected USB devices found", EMsgType.PASS);
+ }
+ else {
+ logPrinter.print("NS in connected USB devices not found", EMsgType.FAIL);
+ close();
+ return;
+ }
+ // Handle NS device
+ handlerNS = new DeviceHandle();
+ result = LibUsb.open(deviceNS, handlerNS);
+ if (result != LibUsb.SUCCESS) {
+ logPrinter.print("Open NS USB device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ if (result == LibUsb.ERROR_ACCESS)
+ logPrinter.print("Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!\n\n" +
+ "Steps to set 'udev' rules:\n" +
+ "root # vim /etc/udev/rules.d/99-NS.rules\n" +
+ "SUBSYSTEM==\"usb\", ATTRS{idVendor}==\"057e\", ATTRS{idProduct}==\"3000\", GROUP=\"plugdev\"\n" +
+ "root # udevadm control --reload-rules && udevadm trigger\n", EMsgType.INFO);
+ // Let's make a bit dirty workaround since such shit happened
+ logPrinter.print("Requested context close", EMsgType.INFO);
+ LibUsb.exit(contextNS);
+ return; // And close
+ }
+ else
+ logPrinter.print("Open NS USB device", EMsgType.PASS);
+
+ logPrinter.print("Free device list", EMsgType.INFO);
+ LibUsb.freeDeviceList(deviceList, true);
+
+ // DO some stuff to connected NS
+ // Check if this device uses kernel driver and detach if possible:
+ boolean canDetach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER); // if cant, it's windows ot old lib
+ if (canDetach){
+ int usedByKernel = LibUsb.kernelDriverActive(handlerNS, DEFAULT_INTERFACE);
+ if (usedByKernel == LibUsb.SUCCESS)
+ logPrinter.print("Can proceed with libusb driver", EMsgType.PASS); // we're good
+ else if (usedByKernel == 1) { // used by kernel
+ result = LibUsb.detachKernelDriver(handlerNS, DEFAULT_INTERFACE);
+ logPrinter.print("Detach kernel required", EMsgType.INFO);
+ if (result != 0) {
+ logPrinter.print("Detach kernel\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ close();
+ return;
+ }
+ else
+ logPrinter.print("Detach kernel", EMsgType.PASS);
+ }
+ else
+ logPrinter.print("Can't proceed with libusb driver\n Returned: "+UsbErrorCodes.getErrCode(usedByKernel), EMsgType.FAIL);
+ }
+ else
+ logPrinter.print("libusb doesn't support function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. It's normal. Proceeding.", EMsgType.WARNING);
+ /*
+ // Reset device
+ result = LibUsb.resetDevice(handlerNS);
+ if (result == 0)
+ logPrinter.print("Reset device", EMsgType.PASS);
+ else {
+ logPrinter.print("Reset device returned: " + result, EMsgType.FAIL);
+ updateAndClose();
+ return;
+ }
+ */
+ // Set configuration (soft reset if needed)
+ result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need
+ if (result != LibUsb.SUCCESS){
+ logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ close();
+ return;
+ }
+ else
+ logPrinter.print("Set active configuration to device.", EMsgType.PASS);
+
+ // Claim interface
+ result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE);
+ if (result != LibUsb.SUCCESS) {
+ logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
+ close();
+ return;
+ }
+ else
+ logPrinter.print("Claim interface", EMsgType.PASS);
+
+ this.connected = true;
+ }
+
+ /**
+ * Get USB status
+ * @return status of connection
+ */
+ boolean isConnected() { return connected; }
+ /**
+ * Getter for handler
+ * @return DeviceHandle of NS
+ */
+ DeviceHandle getHandlerNS(){ return handlerNS; }
+ /**
+ * Correct exit
+ * */
+ void close(){
+ // Close handler in the end
+ if (handlerNS != null) {
+ // Try to release interface
+ int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
+
+ if (result != LibUsb.SUCCESS)
+ logPrinter.print("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING);
+ else
+ logPrinter.print("Release interface", EMsgType.PASS);
+
+ LibUsb.close(handlerNS);
+ logPrinter.print("Requested handler close", EMsgType.INFO);
+ }
+ // Close context in the end
+ if (contextNS != null) {
+ LibUsb.exit(contextNS);
+ logPrinter.print("Requested context close", EMsgType.INFO);
+ }
+ }
+}