From d1b306757562ffb31c31595e65987aeeaf123f8f Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Wed, 13 Mar 2019 06:54:37 +0300 Subject: [PATCH 01/23] development release with debug tails --- pom.xml | 2 +- src/main/java/nsusbloader/NSLMain.java | 2 +- .../java/nsusbloader/UsbCommunications.java | 30 +++++++++++++++++- src/main/resources/res/app_logo.png | Bin 13651 -> 15469 bytes 4 files changed, 31 insertions(+), 3 deletions(-) diff --git a/pom.xml b/pom.xml index 5665658..02722c0 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ NS-USBloader ns-usbloader - 0.2.2-SNAPSHOT + 0.2.3_DEVELOPMENT-SNAPSHOT https://github.com/developersu/ns-usbloader/ diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index c1b4828..06720a0 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.2.2"; + public static final String appVersion = "v0.2.3_DEVELOPMENT"; @Override public void start(Stage primaryStage) throws Exception{ diff --git a/src/main/java/nsusbloader/UsbCommunications.java b/src/main/java/nsusbloader/UsbCommunications.java index b8f74f9..393a51c 100644 --- a/src/main/java/nsusbloader/UsbCommunications.java +++ b/src/main/java/nsusbloader/UsbCommunications.java @@ -228,7 +228,15 @@ public class UsbCommunications extends Task { } else printLog("libusb doesn't supports function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. Proceeding.", EMsgType.WARNING); - + // Reset device + result = LibUsb.resetDevice(handlerNS); + if (result == 0) + printLog("Reset device", EMsgType.PASS); + else { + printLog("Reset device returned: " + result, EMsgType.FAIL); + close(); + return null; + } // Set configuration (soft reset if needed) result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need if (result != LibUsb.SUCCESS){ @@ -256,7 +264,27 @@ public class UsbCommunications extends Task { printLog("Set active configuration to device.", EMsgType.PASS); } + ////////////////////////////////////////// DEBUG INFORMATION START /////////////////////////////////////////// + // + ConfigDescriptor configDescriptor = new ConfigDescriptor(); + //result = LibUsb.getConfigDescriptor(deviceNS, (byte)0x01, configDescriptor); + result = LibUsb.getActiveConfigDescriptor(deviceNS, configDescriptor); + switch (result){ + case 0: + printLog("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); + break; + case LibUsb.ERROR_NOT_FOUND: + printLog("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); + break; + default: + printLog("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); + break; + } + + LibUsb.freeConfigDescriptor(configDescriptor); + //*/ + ////////////////////////////////////////// DEBUG INFORMATION END ////////////////////////////////////////////// // Claim interface result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) { diff --git a/src/main/resources/res/app_logo.png b/src/main/resources/res/app_logo.png index 53c27bb4f05c019ff2526a4b20ed783729f870b5..8a9748c8728bb7cedcb04892a46cdcd5a29243dd 100644 GIT binary patch literal 15469 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 From bda316abbbfa0a7ff0dad5a4f02c983ec745e79a Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Wed, 13 Mar 2019 17:04:14 +0300 Subject: [PATCH 02/23] development release: Using usb4java 1.2.0 to check if it works @ macOS 'High Sierra' --- pom.xml | 16 +++++++-- src/main/java/nsusbloader/NSLMain.java | 2 +- .../java/nsusbloader/UsbCommunications.java | 33 ++++--------------- 3 files changed, 21 insertions(+), 30 deletions(-) diff --git a/pom.xml b/pom.xml index 02722c0..918b010 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ NS-USBloader ns-usbloader - 0.2.3_DEVELOPMENT-SNAPSHOT + 0.2.3_DEV-SNAPSHOT https://github.com/developersu/ns-usbloader/ @@ -140,7 +140,7 @@ org.usb4java usb4java - 1.3.0 + 1.2.0 compile @@ -155,6 +155,18 @@ 1.8 + + + maven-jar-plugin + 2.4 + + + default-jar + none + + + + org.apache.maven.plugins maven-assembly-plugin diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index 06720a0..7fd6cba 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.2.3_DEVELOPMENT"; + public static final String appVersion = "v0.2.3_DEV"; @Override public void start(Stage primaryStage) throws Exception{ diff --git a/src/main/java/nsusbloader/UsbCommunications.java b/src/main/java/nsusbloader/UsbCommunications.java index 393a51c..abd9301 100644 --- a/src/main/java/nsusbloader/UsbCommunications.java +++ b/src/main/java/nsusbloader/UsbCommunications.java @@ -114,19 +114,17 @@ public class UsbCommunications extends Task { switch (result){ case 0: - System.out.println("SUCCES"); - System.out.println("\n"+configDescriptor.dump()); + printLog("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); break; case LibUsb.ERROR_NOT_FOUND: - System.out.println("ERROR_NOT_FOUND "+result); + printLog("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); break; default: - System.out.println("UNKNOWN "+result); + printLog("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); break; } - System.out.println(); - //LibUsb.freeConfigDescriptor(configDescriptor); + LibUsb.freeConfigDescriptor(configDescriptor); //*/ /* * So what did we learn? @@ -228,6 +226,7 @@ public class UsbCommunications extends Task { } else printLog("libusb doesn't supports function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. Proceeding.", EMsgType.WARNING); + /* // Reset device result = LibUsb.resetDevice(handlerNS); if (result == 0) @@ -237,6 +236,7 @@ public class UsbCommunications extends Task { close(); return null; } + */ // Set configuration (soft reset if needed) result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need if (result != LibUsb.SUCCESS){ @@ -264,27 +264,6 @@ public class UsbCommunications extends Task { printLog("Set active configuration to device.", EMsgType.PASS); } - ////////////////////////////////////////// DEBUG INFORMATION START /////////////////////////////////////////// - // - ConfigDescriptor configDescriptor = new ConfigDescriptor(); - //result = LibUsb.getConfigDescriptor(deviceNS, (byte)0x01, configDescriptor); - result = LibUsb.getActiveConfigDescriptor(deviceNS, configDescriptor); - - switch (result){ - case 0: - printLog("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); - break; - case LibUsb.ERROR_NOT_FOUND: - printLog("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); - break; - default: - printLog("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); - break; - } - - LibUsb.freeConfigDescriptor(configDescriptor); - //*/ - ////////////////////////////////////////// DEBUG INFORMATION END ////////////////////////////////////////////// // Claim interface result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) { From 00db6984d962413f1f3e06e14511f1d4e35d251c Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Thu, 14 Mar 2019 21:28:20 +0300 Subject: [PATCH 03/23] Typos, minor fixes, new locale/resourceBundle support, code-refactoring --- README.md | 16 +- pom.xml | 4 +- .../Controllers/NSLMainController.java | 4 +- .../java/nsusbloader/MediatorControl.java | 2 +- src/main/java/nsusbloader/NSLMain.java | 9 +- src/main/java/nsusbloader/USB/LogPrinter.java | 63 +++ .../{ => USB}/MessagesConsumer.java | 5 +- .../nsusbloader/{ => USB}/PFS/NCAFile.java | 2 +- .../{ => USB}/PFS/PFSProvider.java | 83 ++-- .../{ => USB}/UsbCommunications.java | 371 ++++++------------ .../java/nsusbloader/USB/UsbErrorCodes.java | 38 ++ ...locale_en.properties => locale.properties} | 0 ...le_ru.properties => locale_rus.properties} | 2 +- src/main/resources/locale_ukr.properties | 21 + 14 files changed, 296 insertions(+), 324 deletions(-) create mode 100644 src/main/java/nsusbloader/USB/LogPrinter.java rename src/main/java/nsusbloader/{ => USB}/MessagesConsumer.java (96%) rename src/main/java/nsusbloader/{ => USB}/PFS/NCAFile.java (97%) rename src/main/java/nsusbloader/{ => USB}/PFS/PFSProvider.java (70%) rename src/main/java/nsusbloader/{ => USB}/UsbCommunications.java (65%) create mode 100644 src/main/java/nsusbloader/USB/UsbErrorCodes.java rename src/main/resources/{locale_en.properties => locale.properties} (100%) rename src/main/resources/{locale_ru.properties => locale_rus.properties} (87%) create mode 100644 src/main/resources/locale_ukr.properties diff --git a/README.md b/README.md index eeddcb1..4c7d899 100644 --- a/README.md +++ b/README.md @@ -35,16 +35,20 @@ JRE 8u60 or higher. See below. ### macOS -See 'Linux' section. +Double-click on downloaded .jar file. Follow instructions. Or see 'Linux' section. Set 'Security & Privacy' settings if needed. +If you use different MacOS (not Mojave) - check release section for another JAR file. + ### Windows: * Download Zadig: https://zadig.akeo.ie/ -* Open tinfoil. Set 'Title Managment' -> 'Usb install NSP' +* Open tinfoil. Set 'Title Management' -> 'Usb install NSP' * Connect NS to PC -* Open Zadig, select NS in dropdown, select 'libusbK (v3.0.7.0)' (version may vary), click 'Install WCID Driver' +* Open Zadig +* Click 'Options' and select 'List All Devices' +* Select NS in dropdown, select 'libusbK (v3.0.7.0)' (version may vary), click 'Install WCID Driver' * Check that in device list of you system you have 'libusbK USB Devices' folder and your NS inside of it * Download and install Java JRE (8+) * Get this application (JAR file) double-click on on it (alternatively open 'cmd', go to place where jar located and execute via `java -jar thisAppName.jar`) @@ -66,10 +70,10 @@ Table 'Status' = 'Uploaded' does not means that file installed. It means that it Handling successful/failed installation is a purpose of the other side application (TinFoil/GoldLeaf). (And they don't provide any feedback interfaces so I can't detect success/failure.) ## TODO: -- [x] macOS QA v0.1 -- [ ] macOS QA v0.2 (partly) +- [x] macOS QA v0.1 (Mojave) +- [x] macOS QA v0.2.2 (Mojave) - [x] Windows support -- [ ] code refactoring (almost. todo: printLog() ) +- [x] code refactoring - [x] GoldLeaf support - [ ] XCI support - [ ] File order sort (non-critical) diff --git a/pom.xml b/pom.xml index 918b010..7e5c26a 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ NS-USBloader ns-usbloader - 0.2.3_DEV-SNAPSHOT + 0.3_DEV-SNAPSHOT https://github.com/developersu/ns-usbloader/ @@ -140,7 +140,7 @@ org.usb4java usb4java - 1.2.0 + 1.3.0 compile diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index 3befcbd..04c1522 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -11,7 +11,7 @@ import javafx.stage.FileChooser; import nsusbloader.AppPreferences; import nsusbloader.MediatorControl; import nsusbloader.NSLMain; -import nsusbloader.UsbCommunications; +import nsusbloader.USB.UsbCommunications; import java.io.File; import java.net.URL; @@ -137,7 +137,7 @@ public class NSLMainController implements Initializable { if (usbThread == null || !usbThread.isAlive()){ List nspToUpload; if ((nspToUpload = tableFilesListController.getFiles()) == null) { - resourceBundle.getString("logsNoFolderFileSelected"); + logArea.setText(resourceBundle.getString("logsNoFolderFileSelected")); return; }else { logArea.setText(resourceBundle.getString("logsFilesToUploadTitle")+"\n"); diff --git a/src/main/java/nsusbloader/MediatorControl.java b/src/main/java/nsusbloader/MediatorControl.java index 5544864..e828571 100644 --- a/src/main/java/nsusbloader/MediatorControl.java +++ b/src/main/java/nsusbloader/MediatorControl.java @@ -18,7 +18,7 @@ public class MediatorControl { public void setController(NSLMainController controller){ this.applicationController = controller; } - NSLMainController getContoller(){ return this.applicationController; } + public NSLMainController getContoller(){ return this.applicationController; } public synchronized void setTransferActive(boolean state) { isTransferActive.set(state); diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index 7fd6cba..ca8f8f3 100644 --- a/src/main/java/nsusbloader/NSLMain.java +++ b/src/main/java/nsusbloader/NSLMain.java @@ -12,17 +12,16 @@ import java.util.Locale; import java.util.ResourceBundle; public class NSLMain extends Application { - public static final String appVersion = "v0.2.3_DEV"; + public static final String appVersion = "v0.3_DEV"; @Override public void start(Stage primaryStage) throws Exception{ FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml")); ResourceBundle rb; - if (Locale.getDefault().getISO3Language().equals("rus")) - rb = ResourceBundle.getBundle("locale", new Locale("ru")); - else - rb = ResourceBundle.getBundle("locale", new Locale("en")); + Locale userLocale = new Locale(Locale.getDefault().getISO3Language()); // NOTE: user locale based on ISO3 Language codes + rb = ResourceBundle.getBundle("locale", userLocale); + loader.setResources(rb); Parent root = loader.load(); diff --git a/src/main/java/nsusbloader/USB/LogPrinter.java b/src/main/java/nsusbloader/USB/LogPrinter.java new file mode 100644 index 0000000..6faed8a --- /dev/null +++ b/src/main/java/nsusbloader/USB/LogPrinter.java @@ -0,0 +1,63 @@ +package nsusbloader.USB; + +import nsusbloader.NSLDataTypes.EFileStatus; +import nsusbloader.NSLDataTypes.EMsgType; + +import java.io.File; +import java.util.HashMap; +import java.util.concurrent.BlockingQueue; +import java.util.concurrent.LinkedBlockingQueue; + +public class LogPrinter { + private MessagesConsumer msgConsumer; + private BlockingQueue msgQueue; + private BlockingQueue progressQueue; + private HashMap statusMap; // BlockingQueue for literally one object. TODO: read more books ; replace to hashMap + + LogPrinter(){ + this.msgQueue = new LinkedBlockingQueue<>(); + this.progressQueue = new LinkedBlockingQueue<>(); + this.statusMap = new HashMap<>(); + this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue, this.statusMap); + this.msgConsumer.start(); + } + /** + * This is what will print to textArea of the application. + * */ + public void print(String message, EMsgType type){ + try { + switch (type){ + case PASS: + msgQueue.put("[ PASS ] "+message+"\n"); + break; + case FAIL: + msgQueue.put("[ FAIL ] "+message+"\n"); + break; + case INFO: + msgQueue.put("[ INFO ] "+message+"\n"); + break; + case WARNING: + msgQueue.put("[ WARN ] "+message+"\n"); + break; + default: + msgQueue.put(message); + } + }catch (InterruptedException ie){ + ie.printStackTrace(); + } + } + /** + * Update progress for progress bar + * */ + public void updateProgress(Double value) throws InterruptedException{ + progressQueue.put(value); + } + /** + * When we're done + * */ + public void updateAndClose(HashMap nspMap, EFileStatus status){ + for (String fileName: nspMap.keySet()) + statusMap.put(fileName, status); + msgConsumer.interrupt(); + } +} diff --git a/src/main/java/nsusbloader/MessagesConsumer.java b/src/main/java/nsusbloader/USB/MessagesConsumer.java similarity index 96% rename from src/main/java/nsusbloader/MessagesConsumer.java rename to src/main/java/nsusbloader/USB/MessagesConsumer.java index 303580a..d8bd818 100644 --- a/src/main/java/nsusbloader/MessagesConsumer.java +++ b/src/main/java/nsusbloader/USB/MessagesConsumer.java @@ -1,10 +1,11 @@ -package nsusbloader; +package nsusbloader.USB; import javafx.animation.AnimationTimer; import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressIndicator; import javafx.scene.control.TextArea; import nsusbloader.Controllers.NSTableViewController; +import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EFileStatus; import java.util.ArrayList; @@ -69,7 +70,7 @@ public class MessagesConsumer extends AnimationTimer { } } - void interrupt(){ + public void interrupt(){ this.isInterrupted = true; } } \ No newline at end of file diff --git a/src/main/java/nsusbloader/PFS/NCAFile.java b/src/main/java/nsusbloader/USB/PFS/NCAFile.java similarity index 97% rename from src/main/java/nsusbloader/PFS/NCAFile.java rename to src/main/java/nsusbloader/USB/PFS/NCAFile.java index ea537ce..9b52e12 100644 --- a/src/main/java/nsusbloader/PFS/NCAFile.java +++ b/src/main/java/nsusbloader/USB/PFS/NCAFile.java @@ -1,4 +1,4 @@ -package nsusbloader.PFS; +package nsusbloader.USB.PFS; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/src/main/java/nsusbloader/PFS/PFSProvider.java b/src/main/java/nsusbloader/USB/PFS/PFSProvider.java similarity index 70% rename from src/main/java/nsusbloader/PFS/PFSProvider.java rename to src/main/java/nsusbloader/USB/PFS/PFSProvider.java index 6888b80..bd627cc 100644 --- a/src/main/java/nsusbloader/PFS/PFSProvider.java +++ b/src/main/java/nsusbloader/USB/PFS/PFSProvider.java @@ -1,5 +1,6 @@ -package nsusbloader.PFS; +package nsusbloader.USB.PFS; +import nsusbloader.USB.LogPrinter; import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.ServiceWindow; @@ -8,15 +9,14 @@ import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.charset.StandardCharsets; import java.util.*; -import java.util.concurrent.BlockingQueue; /** * Used in GoldLeaf USB protocol * */ public class PFSProvider { private static final byte[] PFS0 = new byte[]{(byte)0x50, (byte)0x46, (byte)0x53, (byte)0x30}; // PFS0, and what did you think? - - private BlockingQueue msgQueue; + + private LogPrinter logPrinter; private ResourceBundle rb; private RandomAccessFile randAccessFile; @@ -25,20 +25,18 @@ public class PFSProvider { private long bodySize; private int ticketID = -1; - public PFSProvider(File nspFile, BlockingQueue msgQueue){ - this.msgQueue = msgQueue; + public PFSProvider(File nspFile, LogPrinter logPrinter){ + this.logPrinter = logPrinter; try { this.randAccessFile = new RandomAccessFile(nspFile, "r"); nspFileName = nspFile.getName(); } catch (FileNotFoundException fnfe){ - printLog("PFS File not founnd: \n "+fnfe.getMessage(), EMsgType.FAIL); + logPrinter.print("PFS File not founnd: \n "+fnfe.getMessage(), EMsgType.FAIL); nspFileName = null; } - if (Locale.getDefault().getISO3Language().equals("rus")) - rb = ResourceBundle.getBundle("locale", new Locale("ru")); - else - rb = ResourceBundle.getBundle("locale", new Locale("en")); + Locale userLocale = new Locale(Locale.getDefault().getISO3Language()); + rb = ResourceBundle.getBundle("locale", userLocale); } public boolean init() { @@ -48,22 +46,22 @@ public class PFSProvider { int filesCount; int header; - printLog("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO); + logPrinter.print("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO); try { byte[] fileStartingBytes = new byte[12]; // Read PFS0, files count, header, padding (4 zero bytes) if (randAccessFile.read(fileStartingBytes) == 12) - printLog("PFS Read file starting bytes.", EMsgType.PASS); + logPrinter.print("PFS Read file starting bytes.", EMsgType.PASS); else { - printLog("PFS Read file starting bytes.", EMsgType.FAIL); + logPrinter.print("PFS Read file starting bytes.", EMsgType.FAIL); randAccessFile.close(); return false; } // Check PFS0 if (Arrays.equals(PFS0, Arrays.copyOfRange(fileStartingBytes, 0, 4))) - printLog("PFS Read 'PFS0'.", EMsgType.PASS); + logPrinter.print("PFS Read 'PFS0'.", EMsgType.PASS); else { - printLog("PFS Read 'PFS0'.", EMsgType.WARNING); + logPrinter.print("PFS Read 'PFS0'.", EMsgType.WARNING); if (!ServiceWindow.getConfirmationWindow(nspFileName+"\n"+rb.getString("windowTitleConfirmWrongPFS0"), rb.getString("windowBodyConfirmWrongPFS0"))) { randAccessFile.close(); return false; @@ -72,19 +70,19 @@ public class PFSProvider { // Get files count filesCount = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 4, 8)).order(ByteOrder.LITTLE_ENDIAN).getInt(); if (filesCount > 0 ) { - printLog("PFS Read files count [" + filesCount + "]", EMsgType.PASS); + logPrinter.print("PFS Read files count [" + filesCount + "]", EMsgType.PASS); } else { - printLog("PFS Read files count", EMsgType.FAIL); + logPrinter.print("PFS Read files count", EMsgType.FAIL); randAccessFile.close(); return false; } // Get header header = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 8, 12)).order(ByteOrder.LITTLE_ENDIAN).getInt(); if (header > 0 ) - printLog("PFS Read header ["+header+"]", EMsgType.PASS); + logPrinter.print("PFS Read header ["+header+"]", EMsgType.PASS); else { - printLog("PFS Read header ", EMsgType.FAIL); + logPrinter.print("PFS Read header ", EMsgType.FAIL); randAccessFile.close(); return false; } @@ -103,10 +101,10 @@ public class PFSProvider { for (int i=0; i= 0?EMsgType.PASS:EMsgType.WARNING); - printLog(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING); - printLog(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING); + logPrinter.print(" Padding check", offset == 0?EMsgType.PASS:EMsgType.WARNING); + logPrinter.print(" NCA offset check: "+nca_offset, nca_offset >= 0?EMsgType.PASS:EMsgType.WARNING); + logPrinter.print(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING); + logPrinter.print(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING); NCAFile ncaFile = new NCAFile(); ncaFile.setNcaOffset(nca_offset); @@ -130,15 +128,15 @@ public class PFSProvider { // Final offset byte[] bufForInt = new byte[4]; if ((randAccessFile.read(bufForInt) == 4) && (Arrays.equals(bufForInt, new byte[4]))) - printLog("PFS Final padding check", EMsgType.PASS); + logPrinter.print("PFS Final padding check", EMsgType.PASS); else - printLog("PFS Final padding check", EMsgType.WARNING); + logPrinter.print("PFS Final padding check", EMsgType.WARNING); // Calculate position including header for body size offset bodySize = randAccessFile.getFilePointer()+header; //********************************************************************************************* // Collect file names from NCAs - printLog("PFS Collecting file names", EMsgType.INFO); + logPrinter.print("PFS Collecting file names", EMsgType.INFO); List ncaFN; // Temporary byte[] b = new byte[1]; // Temporary for (int i=0; i { private final int DEFAULT_INTERFACE = 0; - private BlockingQueue msgQueue; - private BlockingQueue progressQueue; - private HashMap statusMap; // BlockingQueue for literally one object. TODO: read more books ; replace to hashMap + private LogPrinter logPrinter; private EFileStatus status = EFileStatus.FAILED; - private MessagesConsumer msgConsumer; - private HashMap nspMap; private Context contextNS; @@ -52,39 +44,35 @@ public class UsbCommunications extends Task { this.nspMap = new HashMap<>(); for (File f: nspList) nspMap.put(f.getName(), f); - this.msgQueue = new LinkedBlockingQueue<>(); - this.progressQueue = new LinkedBlockingQueue<>(); - this.statusMap = new HashMap<>(); - this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue, this.statusMap); + this.logPrinter = new LogPrinter(); } @Override protected Void call() { - this.msgConsumer.start(); int result = -9999; - printLog("\tStart chain", EMsgType.INFO); + 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) { - printLog("libusb initialization\n Returned: "+result, EMsgType.FAIL); + logPrinter.print("libusb initialization\n Returned: "+result, EMsgType.FAIL); close(); return null; } else - printLog("libusb initialization", EMsgType.PASS); + 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) { - printLog("Get device list\n Returned: "+result, EMsgType.FAIL); + logPrinter.print("Get device list\n Returned: "+result, EMsgType.FAIL); close(); return null; } else { - printLog("Get device list", EMsgType.PASS); + logPrinter.print("Get device list", EMsgType.PASS); } // Searching for NS in devices: looking for NS DeviceDescriptor descriptor; @@ -93,14 +81,14 @@ public class UsbCommunications extends Task { descriptor = new DeviceDescriptor(); // mmm.. leave it as is. result = LibUsb.getDeviceDescriptor(device, descriptor); if (result != LibUsb.SUCCESS){ - printLog("Read file descriptors for USB devices\n Returned: "+result, EMsgType.FAIL); + 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; - printLog("Read file descriptors for USB devices", EMsgType.PASS); + logPrinter.print("Read file descriptors for USB devices", EMsgType.PASS); break; } } @@ -114,13 +102,13 @@ public class UsbCommunications extends Task { switch (result){ case 0: - printLog("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); + logPrinter.print("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); break; case LibUsb.ERROR_NOT_FOUND: - printLog("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); + logPrinter.print("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); break; default: - printLog("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); + logPrinter.print("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); break; } @@ -141,10 +129,10 @@ public class UsbCommunications extends Task { ////////////////////////////////////////// DEBUG INFORMATION END ///////////////////////////////////////////// if (deviceNS != null){ - printLog("NS in connected USB devices found", EMsgType.PASS); + logPrinter.print("NS in connected USB devices found", EMsgType.PASS); } else { - printLog("NS in connected USB devices not found", EMsgType.FAIL); + logPrinter.print("NS in connected USB devices not found", EMsgType.FAIL); close(); return null; } @@ -152,27 +140,16 @@ public class UsbCommunications extends Task { handlerNS = new DeviceHandle(); result = LibUsb.open(deviceNS, handlerNS); if (result != LibUsb.SUCCESS) { - switch (result){ - case LibUsb.ERROR_ACCESS: - printLog("Open NS USB device\n Returned: ERROR_ACCESS", EMsgType.FAIL); - printLog("Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!", EMsgType.INFO); - break; - case LibUsb.ERROR_NO_MEM: - printLog("Open NS USB device\n Returned: ERROR_NO_MEM", EMsgType.FAIL); - break; - case LibUsb.ERROR_NO_DEVICE: - printLog("Open NS USB device\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - default: - printLog("Open NS USB device\n Returned:" + result, EMsgType.FAIL); - } + 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)!", EMsgType.INFO); close(); return null; } else - printLog("Open NS USB device", EMsgType.PASS); + logPrinter.print("Open NS USB device", EMsgType.PASS); - printLog("Free device list", EMsgType.INFO); + logPrinter.print("Free device list", EMsgType.INFO); LibUsb.freeDeviceList(deviceList, true); // DO some stuff to connected NS @@ -180,99 +157,55 @@ public class UsbCommunications extends Task { 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){ - printLog("Can proceed with libusb driver", EMsgType.PASS); // we're good - } - else { - switch (usedByKernel){ - case 1: // used by kernel - result = LibUsb.detachKernelDriver(handlerNS, DEFAULT_INTERFACE); - printLog("Detach kernel required", EMsgType.INFO); - if (result != 0) { - switch (result){ - case LibUsb.ERROR_NOT_FOUND: - printLog("Detach kernel\n Returned: ERROR_NOT_FOUND", EMsgType.FAIL); - break; - case LibUsb.ERROR_INVALID_PARAM: - printLog("Detach kernel\n Returned: ERROR_INVALID_PARAM", EMsgType.FAIL); - break; - case LibUsb.ERROR_NO_DEVICE: - printLog("Detach kernel\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - case LibUsb.ERROR_NOT_SUPPORTED: // Should never appear only if libusb buggy - printLog("Detach kernel\n Returned: ERROR_NOT_SUPPORTED", EMsgType.FAIL); - break; - default: - printLog("Detach kernel\n Returned: " + result, EMsgType.FAIL); - break; - } - close(); - return null; - } - else { - printLog("Detach kernel", EMsgType.PASS); - break; - } - case LibUsb.ERROR_NO_DEVICE: - printLog("Can't proceed with libusb driver\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - case LibUsb.ERROR_NOT_SUPPORTED: - printLog("Can't proceed with libusb driver\n Returned: ERROR_NOT_SUPPORTED", EMsgType.FAIL); - break; - default: - printLog("Can't proceed with libusb driver\n Returned: "+result, EMsgType.FAIL); - } - } + 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 - printLog("libusb doesn't supports function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. Proceeding.", EMsgType.WARNING); + 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) - printLog("Reset device", EMsgType.PASS); + logPrinter.print("Reset device", EMsgType.PASS); else { - printLog("Reset device returned: " + result, EMsgType.FAIL); - close(); + 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){ - switch (result){ - case LibUsb.ERROR_NOT_FOUND: - printLog("Set active configuration to device\n Returned: ERROR_NOT_FOUND", EMsgType.FAIL); - break; - case LibUsb.ERROR_BUSY: - printLog("Set active configuration to device\n Returned: ERROR_BUSY", EMsgType.FAIL); - break; - case LibUsb.ERROR_NO_DEVICE: - printLog("Set active configuration to device\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - case LibUsb.ERROR_INVALID_PARAM: - printLog("Set active configuration to device\n Returned: ERROR_INVALID_PARAM", EMsgType.FAIL); - break; - default: - printLog("Set active configuration to device\n Returned: "+result, EMsgType.FAIL); - break; - } + logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); close(); return null; } else { - printLog("Set active configuration to device.", EMsgType.PASS); + logPrinter.print("Set active configuration to device.", EMsgType.PASS); } // Claim interface result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) { - printLog("Claim interface\n Returned: "+result, EMsgType.FAIL); + logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL); close(); return null; } else - printLog("Claim interface", EMsgType.PASS); + logPrinter.print("Claim interface", EMsgType.PASS); //-------------------------------------------------------------------------------------------------------------- if (protocol.equals("TinFoil")) { @@ -282,7 +215,7 @@ public class UsbCommunications extends Task { } close(); - printLog("\tEnd chain", EMsgType.INFO); + logPrinter.print("\tEnd chain", EMsgType.INFO); return null; } /** @@ -304,11 +237,11 @@ public class UsbCommunications extends Task { // Send list of NSP files: // Proceed "TUL0" if (!writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30} - printLog("TF Send list of files: handshake", EMsgType.FAIL); + logPrinter.print("TF Send list of files: handshake", EMsgType.FAIL); return false; } else - printLog("TF Send list of files: handshake", EMsgType.PASS); + 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()) { @@ -323,24 +256,24 @@ public class UsbCommunications extends Task { //byteBuffer.reset(); // Sending NSP list - printLog("TF Send list of files", EMsgType.INFO); + logPrinter.print("TF Send list of files", EMsgType.INFO); if (!writeToUsb(nspListSize)) { // size of the list we're going to transfer goes... - printLog(" [send list length]", EMsgType.FAIL); + logPrinter.print(" [send list length]", EMsgType.FAIL); return false; } - printLog(" [send list length]", EMsgType.PASS); + logPrinter.print(" [send list length]", EMsgType.PASS); if (!writeToUsb(new byte[8])) { // 8 zero bytes goes... - printLog(" [send padding]", EMsgType.FAIL); + logPrinter.print(" [send padding]", EMsgType.FAIL); return false; } - printLog(" [send padding]", EMsgType.PASS); + logPrinter.print(" [send padding]", EMsgType.PASS); if (!writeToUsb(nspListNames)) { // list of the names goes... - printLog(" [send list itself]", EMsgType.FAIL); + logPrinter.print(" [send list itself]", EMsgType.FAIL); return false; } - printLog(" [send list itself]", EMsgType.PASS); + logPrinter.print(" [send list itself]", EMsgType.PASS); return true; } @@ -348,7 +281,7 @@ public class UsbCommunications extends Task { * After we sent commands to NS, this chain starts * */ private boolean proceedCommands(){ - printLog("TF Awaiting for NS commands.", EMsgType.INFO); + logPrinter.print("TF Awaiting for NS commands.", EMsgType.INFO); /* byte[] magic = new byte[4]; ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic); @@ -370,11 +303,11 @@ public class UsbCommunications extends Task { // 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 - printLog("TF Received EXIT command. Terminating.", EMsgType.PASS); + 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). - printLog("TF Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", EMsgType.PASS); + 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 @@ -414,7 +347,7 @@ public class UsbCommunications extends Task { return false; String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8); - printLog("TF Reply to requested file: "+receivedRequestedNSP + logPrinter.print("TF Reply to requested file: "+receivedRequestedNSP +"\n Range Size: "+receivedRangeSize +"\n Range Offset: "+receivedRangeOffset, EMsgType.INFO); @@ -428,7 +361,7 @@ public class UsbCommunications extends Task { byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb int bufferLength; if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){ - printLog("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL); + logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL); return false; } @@ -446,11 +379,11 @@ public class UsbCommunications extends Task { if (isProgessBarInitiated){ try { if (currentOffset+readPice == receivedRangeOffset){ - progressQueue.put(1.0); + logPrinter.updateProgress(1.0); isProgessBarInitiated = false; } else - progressQueue.put((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0); + logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0); }catch (InterruptedException ie){ getException().printStackTrace(); // TODO: Do something with this } @@ -468,28 +401,28 @@ public class UsbCommunications extends Task { if (bufferLength != -1){ //write to USB if (!writeToUsb(bufferCurrent)) { - printLog("TF Failure during NSP transmission.", EMsgType.FAIL); + logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL); return false; } currentOffset += readPice; } else { - printLog("TF Reading of stream suddenly ended.", EMsgType.WARNING); + logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING); return false; } } bufferedInStream.close(); } catch (FileNotFoundException fnfe){ - printLog("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL); + logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL); fnfe.printStackTrace(); return false; } catch (IOException ioe){ - printLog("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL); + logPrinter.print("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL); ioe.printStackTrace(); return false; } catch (ArithmeticException ae){ - printLog("TF ArithmeticException (can't cast end offset minus current to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL); + logPrinter.print("TF ArithmeticException (can't cast end offset minus current to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL); ae.printStackTrace(); return false; } @@ -502,26 +435,26 @@ public class UsbCommunications extends Task { * 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. - printLog("TF Sending response", EMsgType.INFO); + 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. ){ - printLog(" [1/3]", EMsgType.FAIL); + logPrinter.print(" [1/3]", EMsgType.FAIL); return false; } - printLog(" [1/3]", EMsgType.PASS); + logPrinter.print(" [1/3]", EMsgType.PASS); if(!writeToUsb(rangeSize)) { // Send EXACTLY what has been received - printLog(" [2/3]", EMsgType.FAIL); + logPrinter.print(" [2/3]", EMsgType.FAIL); return false; } - printLog(" [2/3]", EMsgType.PASS); + logPrinter.print(" [2/3]", EMsgType.PASS); if(!writeToUsb(new byte[12])) { // kinda another one padding - printLog(" [3/3]", EMsgType.FAIL); + logPrinter.print(" [3/3]", EMsgType.FAIL); return false; } - printLog(" [3/3]", EMsgType.PASS); + logPrinter.print(" [3/3]", EMsgType.PASS); return true; } @@ -542,14 +475,14 @@ public class UsbCommunications extends Task { private final byte[] CMD_Finish = new byte[]{0x47, 0x4c, 0x55, 0x43, 0x07, 0x00, 0x00, 0x00}; GoldLeaf(){ - printLog("===========================================================================", EMsgType.INFO); - PFSProvider pfsElement = new PFSProvider(nspMap.get(nspMap.keySet().toArray()[0]), msgQueue); + logPrinter.print("===========================================================================", EMsgType.INFO); + PFSProvider pfsElement = new PFSProvider(nspMap.get(nspMap.keySet().toArray()[0]), logPrinter); if (!pfsElement.init()) { - printLog("GL File provided have incorrect structure and won't be uploaded", EMsgType.FAIL); + logPrinter.print("GL File provided have incorrect structure and won't be uploaded", EMsgType.FAIL); status = EFileStatus.INCORRECT_FILE_FAILED; return; } - printLog("GL File structure validated and it will be uploaded", EMsgType.PASS); + logPrinter.print("GL File structure validated and it will be uploaded", EMsgType.PASS); if (initGoldLeafProtocol(pfsElement)) status = EFileStatus.UPLOADED; // else - no change status that is already set to FAILED @@ -560,9 +493,9 @@ public class UsbCommunications extends Task { // Go connect to GoldLeaf if (writeToUsb(CMD_ConnectionRequest)) - printLog("GL Initiating GoldLeaf connection", EMsgType.PASS); + logPrinter.print("GL Initiating GoldLeaf connection", EMsgType.PASS); else { - printLog("GL Initiating GoldLeaf connection", EMsgType.FAIL); + logPrinter.print("GL Initiating GoldLeaf connection", EMsgType.FAIL); return false; } while (true) { @@ -595,7 +528,7 @@ public class UsbCommunications extends Task { continue; } if (Arrays.equals(readByte, CMD_Finish)) { - printLog("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS); + logPrinter.print("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS); break; } } @@ -605,24 +538,24 @@ public class UsbCommunications extends Task { * ConnectionResponse command handler * */ private boolean handleConnectionResponse(PFSProvider pfsElement){ - printLog("GL 'ConnectionResonse' command:", EMsgType.INFO); + logPrinter.print("GL 'ConnectionResponse' command:", EMsgType.INFO); if (!writeToUsb(CMD_NSPName)) { - printLog(" [1/3]", EMsgType.FAIL); + logPrinter.print(" [1/3]", EMsgType.FAIL); return false; } - printLog(" [1/3]", EMsgType.PASS); + logPrinter.print(" [1/3]", EMsgType.PASS); if (!writeToUsb(pfsElement.getBytesNspFileNameLength())) { - printLog(" [2/3]", EMsgType.FAIL); + logPrinter.print(" [2/3]", EMsgType.FAIL); return false; } - printLog(" [2/3]", EMsgType.PASS); + logPrinter.print(" [2/3]", EMsgType.PASS); if (!writeToUsb(pfsElement.getBytesNspFileName())) { - printLog(" [3/3]", EMsgType.FAIL); + logPrinter.print(" [3/3]", EMsgType.FAIL); return false; } - printLog(" [3/3]", EMsgType.PASS); + logPrinter.print(" [3/3]", EMsgType.PASS); return true; } @@ -630,43 +563,43 @@ public class UsbCommunications extends Task { * Start command handler * */ private boolean handleStart(PFSProvider pfsElement){ - printLog("GL Handle 'Start' command:", EMsgType.INFO); + logPrinter.print("GL Handle 'Start' command:", EMsgType.INFO); if (!writeToUsb(CMD_NSPData)) { - printLog(" [Send command]", EMsgType.FAIL); + logPrinter.print(" [Send command]", EMsgType.FAIL); return false; } - printLog(" [Send command]", EMsgType.PASS); + logPrinter.print(" [Send command]", EMsgType.PASS); if (!writeToUsb(pfsElement.getBytesCountOfNca())) { - printLog(" [Send length]", EMsgType.FAIL); + logPrinter.print(" [Send length]", EMsgType.FAIL); return false; } - printLog(" [Send length]", EMsgType.PASS); + logPrinter.print(" [Send length]", EMsgType.PASS); int ncaCount = pfsElement.getIntCountOfNca(); - printLog(" [Send information for "+ncaCount+" files]", EMsgType.INFO); + logPrinter.print(" [Send information for "+ncaCount+" files]", EMsgType.INFO); for (int i = 0; i < ncaCount; i++){ if (!writeToUsb(pfsElement.getNca(i).getNcaFileNameLength())) { - printLog(" [1/4] File #"+i, EMsgType.FAIL); + logPrinter.print(" [1/4] File #"+i, EMsgType.FAIL); return false; } - printLog(" [1/4] File #"+i, EMsgType.PASS); + logPrinter.print(" [1/4] File #"+i, EMsgType.PASS); if (!writeToUsb(pfsElement.getNca(i).getNcaFileName())) { - printLog(" [2/4] File #"+i, EMsgType.FAIL); + logPrinter.print(" [2/4] File #"+i, EMsgType.FAIL); return false; } - printLog(" [2/4] File #"+i, EMsgType.PASS); + logPrinter.print(" [2/4] File #"+i, EMsgType.PASS); if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real. - printLog(" [2/4] File #"+i, EMsgType.FAIL); + logPrinter.print(" [2/4] File #"+i, EMsgType.FAIL); return false; } - printLog(" [3/4] File #"+i, EMsgType.PASS); + logPrinter.print(" [3/4] File #"+i, EMsgType.PASS); if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size - printLog(" [4/4] File #"+i, EMsgType.FAIL); + logPrinter.print(" [4/4] File #"+i, EMsgType.FAIL); return false; } - printLog(" [4/4] File #"+i, EMsgType.PASS); + logPrinter.print(" [4/4] File #"+i, EMsgType.PASS); } return true; } @@ -679,18 +612,18 @@ public class UsbCommunications extends Task { int requestedNcaID; boolean isProgessBarInitiated = false; if (isItRawRequest) { - printLog("GL Handle 'Content' command", EMsgType.INFO); + logPrinter.print("GL Handle 'Content' command", EMsgType.INFO); byte[] readByte = readFromUsb(); if (readByte == null || readByte.length != 4) { - printLog(" [Read requested ID]", EMsgType.FAIL); + logPrinter.print(" [Read requested ID]", EMsgType.FAIL); return false; } requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt(); - printLog(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS); + logPrinter.print(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS); } else { requestedNcaID = pfsElement.getNcaTicketID(); - printLog("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO); + logPrinter.print("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO); } long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize(); @@ -723,11 +656,11 @@ public class UsbCommunications extends Task { if (isProgessBarInitiated){ try { if (readFrom+readPice == realNcaSize){ - progressQueue.put(1.0); + logPrinter.updateProgress(1.0); isProgessBarInitiated = false; } else - progressQueue.put((readFrom+readPice)/(realNcaSize/100.0) / 100.0); + logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0); }catch (InterruptedException ie){ getException().printStackTrace(); // TODO: Do something with this } @@ -742,7 +675,7 @@ public class UsbCommunications extends Task { bufferedInStream.close(); } catch (IOException ioe){ - printLog(" Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL); + logPrinter.print(" Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL); ioe.printStackTrace(); return false; } @@ -754,30 +687,27 @@ public class UsbCommunications extends Task { * Correct exit * */ private void close(){ - // close handler in the end + // Close handler in the end if (handlerNS != null) { // Try to release interface int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE); if (result != LibUsb.SUCCESS) - printLog("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING); + logPrinter.print("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING); else - printLog("Release interface", EMsgType.PASS); + logPrinter.print("Release interface", EMsgType.PASS); LibUsb.close(handlerNS); - printLog("Requested handler close", EMsgType.INFO); + logPrinter.print("Requested handler updateAndClose", EMsgType.INFO); } - // close context in the end + // Close context in the end if (contextNS != null) { LibUsb.exit(contextNS); - printLog("Requested context close", EMsgType.INFO); + logPrinter.print("Requested context updateAndClose", EMsgType.INFO); } - // Report status - for (String fileName: nspMap.keySet()) - statusMap.put(fileName, status); - - msgConsumer.interrupt(); + // Report status and close + logPrinter.updateAndClose(nspMap, status); } /** * Sending any byte array to USB device @@ -792,27 +722,12 @@ public class UsbCommunications extends Task { 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){ - switch (result){ - case LibUsb.ERROR_TIMEOUT: - printLog("Data transfer (write) issue\n Returned: ERROR_TIMEOUT", EMsgType.FAIL); - break; - case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here.. - printLog("Data transfer (write) issue\n Returned: ERROR_PIPE", EMsgType.FAIL); - break; - case LibUsb.ERROR_OVERFLOW: - printLog("Data transfer (write) issue\n Returned: ERROR_OVERFLOW", EMsgType.FAIL); - break; - case LibUsb.ERROR_NO_DEVICE: - printLog("Data transfer (write) issue\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - default: - printLog("Data transfer (write) issue\n Returned: "+result, EMsgType.FAIL); - } - printLog("Execution stopped", EMsgType.FAIL); + logPrinter.print("Data transfer (write) issue\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + logPrinter.print("Execution stopped", EMsgType.FAIL); return false; }else { if (writeBufTransferred.get() != message.length){ - printLog("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL); + logPrinter.print("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL); return false; } else { @@ -834,26 +749,8 @@ public class UsbCommunications extends Task { result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 if (result != LibUsb.SUCCESS){ - switch (result){ - case LibUsb.ERROR_TIMEOUT: - printLog("Data transfer (read) issue\n Returned: ERROR_TIMEOUT", EMsgType.FAIL); - break; - case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here.. - printLog("Data transfer (read) issue\n Returned: ERROR_PIPE", EMsgType.FAIL); - break; - case LibUsb.ERROR_OVERFLOW: - printLog("Data transfer (read) issue\n Returned: ERROR_OVERFLOW", EMsgType.FAIL); - break; - case LibUsb.ERROR_NO_DEVICE: - printLog("Data transfer (read) issue\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL); - break; - case LibUsb.ERROR_IO: - printLog("Data transfer (read) issue\n Returned: ERROR_IO", EMsgType.FAIL); - break; - default: - printLog("Data transfer (read) issue\n Returned: "+result, EMsgType.FAIL); - } - printLog("Execution stopped", EMsgType.FAIL); + 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(); @@ -865,30 +762,4 @@ public class UsbCommunications extends Task { return receivedBytes; } } - - /** - * This is what will print to textArea of the application. - * */ - private void printLog(String message, EMsgType type){ - try { - switch (type){ - case PASS: - msgQueue.put("[ PASS ] "+message+"\n"); - break; - case FAIL: - msgQueue.put("[ FAIL ] "+message+"\n"); - break; - case INFO: - msgQueue.put("[ INFO ] "+message+"\n"); - break; - case WARNING: - msgQueue.put("[ WARN ] "+message+"\n"); - break; - default: - msgQueue.put(message); - } - }catch (InterruptedException ie){ - ie.printStackTrace(); - } - } } \ No newline at end of file diff --git a/src/main/java/nsusbloader/USB/UsbErrorCodes.java b/src/main/java/nsusbloader/USB/UsbErrorCodes.java new file mode 100644 index 0000000..3c7ffde --- /dev/null +++ b/src/main/java/nsusbloader/USB/UsbErrorCodes.java @@ -0,0 +1,38 @@ +package nsusbloader.USB; + +import org.usb4java.LibUsb; + +class UsbErrorCodes { + static String getErrCode(int value){ + switch (value){ + case LibUsb.ERROR_ACCESS: + return "ERROR_ACCESS"; + case LibUsb.ERROR_BUSY: + return "ERROR_BUSY"; + case LibUsb.ERROR_INTERRUPTED: + return "ERROR_INTERRUPTED"; + case LibUsb.ERROR_INVALID_PARAM: + return "ERROR_INVALID_PARAM"; + case LibUsb.ERROR_IO: + return "ERROR_IO"; + case LibUsb.ERROR_NO_DEVICE: + return "ERROR_NO_DEVICE"; + case LibUsb.ERROR_NO_MEM: + return "ERROR_NO_MEM"; + case LibUsb.ERROR_NOT_FOUND: + return "ERROR_NOT_FOUND"; + case LibUsb.ERROR_NOT_SUPPORTED: + return "ERROR_NOT_SUPPORTED"; + case LibUsb.ERROR_OTHER: + return "ERROR_OTHER"; + case LibUsb.ERROR_OVERFLOW: + return "ERROR_OVERFLOW"; + case LibUsb.ERROR_PIPE: + return "ERROR_PIPE"; + case LibUsb.ERROR_TIMEOUT: + return "ERROR_TIMEOUT"; + default: + return Integer.toString(value); + } + } +} diff --git a/src/main/resources/locale_en.properties b/src/main/resources/locale.properties similarity index 100% rename from src/main/resources/locale_en.properties rename to src/main/resources/locale.properties diff --git a/src/main/resources/locale_ru.properties b/src/main/resources/locale_rus.properties similarity index 87% rename from src/main/resources/locale_ru.properties rename to src/main/resources/locale_rus.properties index 1c4d268..2c7ed7e 100644 --- a/src/main/resources/locale_ru.properties +++ b/src/main/resources/locale_rus.properties @@ -5,7 +5,7 @@ logsEnteredAsMsg2=\u0427\u0442\u043E\u0431\u044B \u0438\u0437\u0431\u0435\u0436\ logsFilesToUploadTitle=\u0424\u0430\u0439\u043B\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438: logsGreetingsMessage=\u0414\u043E\u0431\u0440\u043E \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C \u0432 NS-USBloader logsNoFolderFileSelected=\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B - \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. -windowBodyConfirmExit=\u0421\u0435\u0439\u0447\u0430\u0441 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u0440\u0435\u0440\u0432\u0451\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0443.\n\u042D\u0442\u043E \u0445\u0443\u0434\u0448\u0435\u0435 \u0447\u0442\u043E \u0442\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0441\u0435\u0439\u0447\u0430\u0441 \u0441\u0434\u0435\u043B\u0430\u0442\u044C.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438 \u0432\u044B\u0439\u0442\u0438? +windowBodyConfirmExit=\u0421\u0435\u0439\u0447\u0430\u0441 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u0440\u0435\u0440\u0432\u0451\u0442 \u0435\u0451.\n\u042D\u0442\u043E \u0445\u0443\u0434\u0448\u0435\u0435 \u0447\u0442\u043E \u0442\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0441\u0435\u0439\u0447\u0430\u0441 \u0441\u0434\u0435\u043B\u0430\u0442\u044C.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438 \u0432\u044B\u0439\u0442\u0438? windowTitleConfirmExit=\u041D\u0435\u0442, \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C! btnStop=\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C logsGreetingsMessage2=--\n\ diff --git a/src/main/resources/locale_ukr.properties b/src/main/resources/locale_ukr.properties new file mode 100644 index 0000000..9f59484 --- /dev/null +++ b/src/main/resources/locale_ukr.properties @@ -0,0 +1,21 @@ +btnFileOpen=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 .NSP \u0444\u0430\u0439\u043B\u0438 +btnUpload=\u0412\u0456\u0434\u0456\u0441\u043B\u0430\u0442\u0438 \u0443 NS +logsEnteredAsMsg1=\u0412\u0438 \u0443\u0432\u0456\u0439\u0448\u043B\u0438 \u044F\u043A: +logsEnteredAsMsg2=\u0414\u043B\u044F \u0437\u0430\u043F\u043E\u0431\u0456\u0433\u0430\u043D\u043D\u044F \u043F\u043E\u043C\u0438\u043B\u043E\u043A \u0432\u0438 \u043C\u0430\u0454\u0442\u0435 \u0431\u0443\u0442\u0438 root \u0430\u0431\u043E \u043D\u0430\u0441\u0442\u0440\u043E\u0457\u0442\u0438 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 'udev' \u0434\u043B\u044F \u0446\u044C\u043E\u0433\u043E \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430. +logsFilesToUploadTitle=\u0424\u0430\u0439\u043B\u0438 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F: +logsGreetingsMessage=\u041B\u0430\u0441\u043A\u0430\u0432\u043E \u043F\u0440\u043E\u0441\u0438\u043C\u043E \u0434\u043E NS-USBloader +logsNoFolderFileSelected=\u0424\u0430\u0439\u043B\u0438 \u043D\u0435 \u0432\u0438\u0431\u0440\u0430\u043D\u0456 - \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0443\u0432\u0430\u0442\u0438 \u043D\u0456\u0447\u043E\u0433\u043E. +windowBodyConfirmExit=\u0417\u0430\u0440\u0430\u0437 \u0432\u0456\u0434\u0431\u0443\u0432\u0430\u0454\u0442\u044C\u0441\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u0438\u0445 \u0456 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043D\u044F \u0434\u043E\u0434\u0430\u0442\u043A\u0443 \u043F\u0440\u0435\u0440\u0432\u0435 \u0457\u0457.\n\u0426\u0435 \u043D\u0430\u0439\u0433\u0456\u0440\u0448\u0435 \u0449\u043E \u0442\u0438 \u043C\u043E\u0436\u0435\u0448 \u0437\u0430\u0440\u0430\u0437 \u0437\u0440\u043E\u0431\u0438\u0442\u0438.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u0438 \u043F\u0440\u043E\u0446\u0435\u0441 \u0456 \u0432\u0438\u0439\u0442\u0438? +windowTitleConfirmExit=\u041D\u0456, \u0437\u0443\u043F\u0438\u043D\u0438\u0441\u044C! +btnStop=\u041F\u0435\u0440\u0435\u0440\u0432\u0430\u0442\u0438 +logsGreetingsMessage2=--\n\ +\u0421\u0438\u0440\u0446\u0435\u0432\u0438\u0439 \u043A\u043E\u0434: https://github.com/developersu/ns-usbloader/\n\ +\u0421\u0430\u0439\u0442: https://developersu.blogspot.com/search/label/NS-USBloader\n\ +\u0418\u0441\u0430\u0454\u043D\u043A\u043E \u0414\u043C\u0438\u0442\u0440\u043E [developer.su] +windowTitleConfirmWrongPFS0=\u041D\u0435\u0432\u0456\u0440\u043D\u0438\u0439 \u0442\u0438\u043F \u0444\u0430\u0439\u043B\u0443 +windowBodyConfirmWrongPFS0=\u0412\u0438\u0431\u0440\u0430\u043D\u0438\u0439 \u0444\u0430\u0439\u043B NSP \u043C\u0430\u0454 \u043D\u0435\u0432\u0456\u0440\u043D\u0456 \u0441\u0438\u043C\u0432\u043E\u043B\u0438. \u0421\u043A\u043E\u0440\u0456\u0448\u0435 \u0437\u0430 \u0432\u0441\u0435 \u0432\u0456\u043D \u043F\u043E\u0448\u043A\u043E\u0434\u0436\u0435\u043D\u0438\u0439.\n\ +\u041A\u0440\u0430\u0449\u0435 \u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438\u0441\u044F \u043F\u0440\u044F\u043C\u043E \u0437\u0430\u0440\u0430\u0437. \u0425\u043E\u0447\u0435\u0448 \u043F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u043D\u0435 \u0437\u0432\u0430\u0436\u0430\u044E\u0447\u0438 \u043D\u0456 \u043D\u0430 \u0449\u043E? +tableStatusLbl=\u0421\u0442\u0430\u043D +tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443 +tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 (~\u041C\u0431) +tableUploadLbl=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436.? \ No newline at end of file From 4cfd651667e4e8ed5c053c19c27c4f4f887bea59 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Thu, 14 Mar 2019 23:41:17 +0300 Subject: [PATCH 04/23] Drag-n-drop support --- README.md | 4 ++ .../Controllers/NSLMainController.java | 50 ++++++++++++++++++- .../Controllers/NSTableViewController.java | 17 ++++++- src/main/java/nsusbloader/NSLMain.java | 4 +- src/main/resources/NSLMain.fxml | 2 +- 5 files changed, 71 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 4c7d899..3c31438 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,10 @@ If you use different MacOS (not Mojave) - check release section for another JAR Table 'Status' = 'Uploaded' does not means that file installed. It means that it has been sent to NS without any issues! That's what this app about. Handling successful/failed installation is a purpose of the other side application (TinFoil/GoldLeaf). (And they don't provide any feedback interfaces so I can't detect success/failure.) +## Translators! Traducteurs! Traductores! Übersetzer! Թարգմանիչներ! +If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it. +Upload somewhere (pastebin? google drive? whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add. + ## TODO: - [x] macOS QA v0.1 (Mojave) - [x] macOS QA v0.2.2 (Mojave) diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index 04c1522..2ae90bd 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -5,6 +5,8 @@ import javafx.collections.ObservableList; import javafx.fxml.FXML; import javafx.fxml.Initializable; import javafx.scene.control.*; +import javafx.scene.input.DragEvent; +import javafx.scene.input.TransferMode; import javafx.scene.layout.Pane; import javafx.scene.layout.Region; import javafx.stage.FileChooser; @@ -15,6 +17,7 @@ import nsusbloader.USB.UsbCommunications; import java.io.File; import java.net.URL; +import java.util.ArrayList; import java.util.List; import java.util.ResourceBundle; @@ -136,7 +139,7 @@ public class NSLMainController implements Initializable { private void uploadBtnAction(){ if (usbThread == null || !usbThread.isAlive()){ List nspToUpload; - if ((nspToUpload = tableFilesListController.getFiles()) == null) { + if ((nspToUpload = tableFilesListController.getFilesForUpload()) == null) { logArea.setText(resourceBundle.getString("logsNoFolderFileSelected")); return; }else { @@ -188,6 +191,51 @@ public class NSLMainController implements Initializable { uploadStopBtn.getStyleClass().add("buttonUp"); } } + /** + * Drag-n-drop support (dragOver consumer) + * */ + @FXML + private void handleDragOver(DragEvent event){ + if (event.getDragboard().hasFiles()) + event.acceptTransferModes(TransferMode.ANY); + } + /** + * Drag-n-drop support (drop consumer) + * */ + @FXML + private void handleDrop(DragEvent event){ + List filesDropped = new ArrayList<>(); + try { + for (File fileOrDir : event.getDragboard().getFiles()) { + if (fileOrDir.getName().toLowerCase().endsWith(".nsp")) + filesDropped.add(fileOrDir); + else if (fileOrDir.isDirectory()) + for (File file : fileOrDir.listFiles()) + if (file.getName().toLowerCase().endsWith(".nsp")) + filesDropped.add(file); + } + } + catch (SecurityException se){ + se.printStackTrace(); + } + if (!filesDropped.isEmpty()) { + List filesAlreadyInTable; + if ((filesAlreadyInTable = tableFilesListController.getFiles()) != null) { + filesDropped.removeAll(filesAlreadyInTable); // Get what we already have and add new file(s) + if (!filesDropped.isEmpty()) { + filesDropped.addAll(tableFilesListController.getFiles()); + tableFilesListController.setFiles(filesDropped); + } + } + else { + tableFilesListController.setFiles(filesDropped); + uploadStopBtn.setDisable(false); + } + } + + event.setDropCompleted(true); + + } /** * Save preferences before exit * */ diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index 9e1670c..6b5a9da 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -124,12 +124,27 @@ public class NSTableViewController implements Initializable { rowsObsLst.get(0).setMarkForUpload(true); } } + /** + * Return all files no matter how they're marked + * */ + public List getFiles(){ + List files = new ArrayList<>(); + if (rowsObsLst.isEmpty()) + return null; + else + for (NSLRowModel model: rowsObsLst) + files.add(model.getNspFile()); + if (!files.isEmpty()) + return files; + else + return null; + } /** * Return files ready for upload. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined * @return null if no files marked for upload * List if there are files * */ - public List getFiles(){ + public List getFilesForUpload(){ List files = new ArrayList<>(); if (rowsObsLst.isEmpty()) return null; diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index ca8f8f3..f07169d 100644 --- a/src/main/java/nsusbloader/NSLMain.java +++ b/src/main/java/nsusbloader/NSLMain.java @@ -18,10 +18,8 @@ public class NSLMain extends Application { FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml")); - ResourceBundle rb; Locale userLocale = new Locale(Locale.getDefault().getISO3Language()); // NOTE: user locale based on ISO3 Language codes - rb = ResourceBundle.getBundle("locale", userLocale); - + ResourceBundle rb = ResourceBundle.getBundle("locale", userLocale); loader.setResources(rb); Parent root = loader.load(); diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index ae1ddba..1ce9f82 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -17,7 +17,7 @@ - + From dc5ba1425b6f1458aca644f7c6986fe00a79269f Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 00:58:04 +0300 Subject: [PATCH 05/23] Size displays now as GB/MB/kB --- .../java/nsusbloader/Controllers/NSLMainController.java | 3 +-- src/main/java/nsusbloader/Controllers/NSLRowModel.java | 7 ++++++- src/main/resources/locale.properties | 2 +- src/main/resources/locale_rus.properties | 2 +- src/main/resources/locale_ukr.properties | 2 +- 5 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index 2ae90bd..e4d9c74 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -223,7 +223,7 @@ public class NSLMainController implements Initializable { if ((filesAlreadyInTable = tableFilesListController.getFiles()) != null) { filesDropped.removeAll(filesAlreadyInTable); // Get what we already have and add new file(s) if (!filesDropped.isEmpty()) { - filesDropped.addAll(tableFilesListController.getFiles()); + filesDropped.addAll(filesAlreadyInTable); tableFilesListController.setFiles(filesDropped); } } @@ -234,7 +234,6 @@ public class NSLMainController implements Initializable { } event.setDropCompleted(true); - } /** * Save preferences before exit diff --git a/src/main/java/nsusbloader/Controllers/NSLRowModel.java b/src/main/java/nsusbloader/Controllers/NSLRowModel.java index afd3f17..8ede8d5 100644 --- a/src/main/java/nsusbloader/Controllers/NSLRowModel.java +++ b/src/main/java/nsusbloader/Controllers/NSLRowModel.java @@ -16,7 +16,12 @@ public class NSLRowModel { this.nspFile = nspFile; this.markForUpload = checkBoxValue; this.nspFileName = nspFile.getName(); - this.nspFileSize = String.format("%.2f", nspFile.length()/1024.0/1024.0); + if (nspFile.length()/1024.0/1024.0/1024.0 > 1) + this.nspFileSize = String.format("%.2f", nspFile.length()/1024.0/1024.0/1024.0)+" GB"; + else if (nspFile.length()/1024.0/1024.0 > 1) + this.nspFileSize = String.format("%.2f", nspFile.length()/1024.0/1024.0)+" MB"; + else + this.nspFileSize = String.format("%.2f", nspFile.length()/1024.0)+" kB"; this.status = ""; } // Model methods start diff --git a/src/main/resources/locale.properties b/src/main/resources/locale.properties index 17ee4b2..b81bc63 100644 --- a/src/main/resources/locale.properties +++ b/src/main/resources/locale.properties @@ -17,5 +17,5 @@ windowBodyConfirmWrongPFS0=Selected NSP file has incrrect starting symbols. Most It's better to interrupt proccess now. Continue process anyway? tableStatusLbl=Status tableFileNameLbl=File name -tableSizeLbl=Size (~Mb) +tableSizeLbl=Size tableUploadLbl=Upload? diff --git a/src/main/resources/locale_rus.properties b/src/main/resources/locale_rus.properties index 2c7ed7e..e215f8e 100644 --- a/src/main/resources/locale_rus.properties +++ b/src/main/resources/locale_rus.properties @@ -17,7 +17,7 @@ windowBodyConfirmWrongPFS0=\u0412\u044B\u0431\u0440\u0430\u043D\u043D\u044B\u043 \u041B\u0443\u0447\u0448\u0435 \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0442\u044C\u0441\u044F \u043F\u0440\u044F\u043C\u043E \u0441\u0435\u0439\u0447\u0430\u0441. \u0425\u043E\u0447\u0435\u0448\u044C \u043F\u0440\u043E\u0434\u043E\u043B\u0436\u0430\u0442\u044C \u043D\u0438 \u0441\u043C\u043E\u0442\u0440\u044F \u043D\u0438 \u043D\u0430 \u0447\u0442\u043E?\ tableUploadLbl=\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C? -tableSizeLbl=\u0420\u0430\u0437\u043C\u0435\u0440 (~\u041C\u0431) +tableSizeLbl=\u0420\u0430\u0437\u043C\u0435\u0440 tableFileNameLbl=\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430 tableStatusLbl=\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 diff --git a/src/main/resources/locale_ukr.properties b/src/main/resources/locale_ukr.properties index 9f59484..ba9e5d5 100644 --- a/src/main/resources/locale_ukr.properties +++ b/src/main/resources/locale_ukr.properties @@ -17,5 +17,5 @@ windowBodyConfirmWrongPFS0=\u0412\u0438\u0431\u0440\u0430\u043D\u0438\u0439 \u04 \u041A\u0440\u0430\u0449\u0435 \u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438\u0441\u044F \u043F\u0440\u044F\u043C\u043E \u0437\u0430\u0440\u0430\u0437. \u0425\u043E\u0447\u0435\u0448 \u043F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u043D\u0435 \u0437\u0432\u0430\u0436\u0430\u044E\u0447\u0438 \u043D\u0456 \u043D\u0430 \u0449\u043E? tableStatusLbl=\u0421\u0442\u0430\u043D tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443 -tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 (~\u041C\u0431) +tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 tableUploadLbl=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436.? \ No newline at end of file From c5f62ed84aeba354e479073f5207b044b9adb3e0 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 04:09:04 +0300 Subject: [PATCH 06/23] Context menu added to the TableView for items. Now it's possible to remove items from the table. --- .../Controllers/NSLMainController.java | 6 +++ .../Controllers/NSTableViewController.java | 52 ++++++++++++++++--- src/main/resources/locale.properties | 2 + src/main/resources/locale_rus.properties | 2 + src/main/resources/locale_ukr.properties | 4 +- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index e4d9c74..569a969 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -191,6 +191,12 @@ public class NSLMainController implements Initializable { uploadStopBtn.getStyleClass().add("buttonUp"); } } + /** + * Crunch. Now you see that I'm not a programmer.. This function called from NSTableViewController + * */ + public void disableUploadStopBtn(){ + uploadStopBtn.setDisable(true); + } /** * Drag-n-drop support (dragOver consumer) * */ diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index 6b5a9da..c11191d 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -1,19 +1,21 @@ package nsusbloader.Controllers; +import javafx.beans.binding.Bindings; import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; +import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; +import javafx.event.ActionEvent; +import javafx.event.EventHandler; import javafx.fxml.FXML; import javafx.fxml.Initializable; -import javafx.scene.control.Label; -import javafx.scene.control.TableCell; -import javafx.scene.control.TableColumn; -import javafx.scene.control.TableView; +import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; import javafx.util.Callback; +import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EFileStatus; import java.io.File; @@ -32,6 +34,7 @@ public class NSTableViewController implements Initializable { @Override public void initialize(URL url, ResourceBundle resourceBundle) { rowsObsLst = FXCollections.observableArrayList(); + table.setPlaceholder(new Label()); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); @@ -87,7 +90,45 @@ public class NSTableViewController implements Initializable { return cell; } }); + table.setRowFactory( // this shit is made to implement context menu. It's such a pain.. + new Callback, TableRow>() { + @Override + public TableRow call(TableView nslRowModelTableView) { + final TableRow row = new TableRow<>(); + ContextMenu contextMenu = new ContextMenu(); + MenuItem deleteMenuItem = new MenuItem(resourceBundle.getString("contextMenuBtnDelete")); + deleteMenuItem.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent actionEvent) { + rowsObsLst.remove(row.getItem()); + if (rowsObsLst.isEmpty()) + MediatorControl.getInstance().getContoller().disableUploadStopBtn(); + table.refresh(); + } + }); + MenuItem deleteAllMenuItem = new MenuItem(resourceBundle.getString("contextMenuBtnDeleteAll")); + deleteAllMenuItem.setOnAction(new EventHandler() { + @Override + public void handle(ActionEvent actionEvent) { + rowsObsLst.clear(); + MediatorControl.getInstance().getContoller().disableUploadStopBtn(); + table.refresh(); + } + }); + contextMenu.getItems().addAll(deleteMenuItem, deleteAllMenuItem); + row.setContextMenu(contextMenu); + row.contextMenuProperty().bind( + Bindings.when( + Bindings.isNotNull( + row.itemProperty())) + .then(MediatorControl.getInstance().getTransferActive()?(ContextMenu)null:contextMenu) + .otherwise((ContextMenu) null) + ); + return row; + } + } + ); table.setItems(rowsObsLst); table.getColumns().addAll(statusColumn, fileNameColumn, fileSizeColumn, uploadColumn); } @@ -108,9 +149,8 @@ public class NSTableViewController implements Initializable { * */ public void setFiles(List files){ rowsObsLst.clear(); // TODO: consider table refresh - if (files == null) { + if (files == null) return; - } if (protocol.equals("TinFoil")){ for (File nspFile: files){ rowsObsLst.add(new NSLRowModel(nspFile, true)); diff --git a/src/main/resources/locale.properties b/src/main/resources/locale.properties index b81bc63..3a61b8f 100644 --- a/src/main/resources/locale.properties +++ b/src/main/resources/locale.properties @@ -19,3 +19,5 @@ tableStatusLbl=Status tableFileNameLbl=File name tableSizeLbl=Size tableUploadLbl=Upload? +contextMenuBtnDelete=Remove +contextMenuBtnDeleteAll=Remove all diff --git a/src/main/resources/locale_rus.properties b/src/main/resources/locale_rus.properties index e215f8e..6869862 100644 --- a/src/main/resources/locale_rus.properties +++ b/src/main/resources/locale_rus.properties @@ -20,4 +20,6 @@ tableUploadLbl=\u0417\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C? tableSizeLbl=\u0420\u0430\u0437\u043C\u0435\u0440 tableFileNameLbl=\u0418\u043C\u044F \u0444\u0430\u0439\u043B\u0430 tableStatusLbl=\u0421\u043E\u0441\u0442\u043E\u044F\u043D\u0438\u0435 +contextMenuBtnDelete=\u0423\u0434\u0430\u043B\u0438\u0442\u044C +contextMenuBtnDeleteAll=\u0423\u0434\u0430\u043B\u0438\u0442\u044C \u0432\u0441\u0451 diff --git a/src/main/resources/locale_ukr.properties b/src/main/resources/locale_ukr.properties index ba9e5d5..4112031 100644 --- a/src/main/resources/locale_ukr.properties +++ b/src/main/resources/locale_ukr.properties @@ -18,4 +18,6 @@ windowBodyConfirmWrongPFS0=\u0412\u0438\u0431\u0440\u0430\u043D\u0438\u0439 \u04 tableStatusLbl=\u0421\u0442\u0430\u043D tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443 tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 -tableUploadLbl=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436.? \ No newline at end of file +tableUploadLbl=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436.? +contextMenuBtnDelete=\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438 +contextMenuBtnDeleteAll=\u0412\u0438\u0434\u0430\u043B\u0438\u0442\u0438 \u0432\u0441\u0435 \ No newline at end of file From f5c37c3771a440c6381513a17b2ad4667f431f62 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 05:34:50 +0300 Subject: [PATCH 07/23] CSS updates for selection in table and context menu. --- .../Controllers/NSTableViewController.java | 3 +-- src/main/resources/res/app_dark.css | 15 +++++++++------ src/main/resources/res/app_light.css | 11 ++++++++--- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index c11191d..01025d7 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -5,7 +5,6 @@ import javafx.beans.property.SimpleBooleanProperty; import javafx.beans.value.ChangeListener; import javafx.beans.value.ObservableValue; import javafx.collections.FXCollections; -import javafx.collections.ListChangeListener; import javafx.collections.ObservableList; import javafx.event.ActionEvent; import javafx.event.EventHandler; @@ -233,4 +232,4 @@ public class NSTableViewController implements Initializable { table.refresh(); } -} +} \ No newline at end of file diff --git a/src/main/resources/res/app_dark.css b/src/main/resources/res/app_dark.css index 046ba9e..41329cf 100644 --- a/src/main/resources/res/app_dark.css +++ b/src/main/resources/res/app_dark.css @@ -193,7 +193,6 @@ .table-view .column-header-background .label{ -fx-background-color: transparent; -fx-text-fill: #08f3ff; - } .table-view .column-header-background, .table-view .filler{ -fx-background-color: #4f4f4f; @@ -202,14 +201,19 @@ .table-view .table-cell{ -fx-text-fill: #f7fafa; } -.table-row-cell, .table-row-cell:filled:selected, .table-row-cell:selected{ +.table-row-cell, .table-row-cell:selected, .table-row-cell:filled:selected{ -fx-background-color: -fx-table-cell-border-color, #424242; -fx-background-insets: 0, 0 0 1 0; -fx-padding: 0.0em; /* 0 */ -fx-table-cell-border-color: #6d8484; } - -.table-row-cell:odd, .table-row-cell:odd:filled:selected, .table-row-cell:odd:selected{ +.table-row-cell .text, .table-row-cell:odd .text{ + -fx-fill: #f7fafa; +} +.table-row-cell:filled:selected .text, .table-row-cell:odd:filled:selected .text{ + -fx-fill: #08f3ff; +} +.table-row-cell:odd, .table-row-cell:odd:selected, .table-row-cell:odd:filled:selected{ -fx-background-color: -fx-table-cell-border-color, #4f4f4f; -fx-background-insets: 0, 0 0 1 0; -fx-padding: 0.0em; /* 0 */ @@ -218,14 +222,13 @@ // -========================== Context menu =====================- .context-menu { -fx-background-color: #2d2d2d; - -fx-text-fill: white; -fx-cursor: hand; } .context-menu .menu-item .label { -fx-text-fill: #f7fafa; } .context-menu .menu-item:focused .label { - -fx-text-fill: #f7fafa; + -fx-text-fill: white; } diff --git a/src/main/resources/res/app_light.css b/src/main/resources/res/app_light.css index 92a11a4..c63ebdc 100644 --- a/src/main/resources/res/app_light.css +++ b/src/main/resources/res/app_light.css @@ -207,7 +207,13 @@ -fx-padding: 0.0em; /* 0 */ -fx-table-cell-border-color: #b0b0b0; } - +.table-row-cell .text, .table-row-cell:odd .text { + -fx-fill: #2c2c2c; +} +.table-row-cell:filled:selected .text, .table-row-cell:odd:filled:selected .text{ + -fx-fill: #2c2c2c; + -fx-font-weight: bold +} .table-row-cell:odd, .table-row-cell:odd:filled:selected, .table-row-cell:odd:selected{ -fx-background-color: -fx-table-cell-border-color, #fefefe; -fx-background-insets: 0, 0 0 1 0; @@ -217,14 +223,13 @@ // -========================== Context menu =====================- .context-menu { -fx-background-color: #fefefe; - -fx-text-fill: white; -fx-cursor: hand; } .context-menu .menu-item .label { -fx-text-fill: #2c2c2c; } .context-menu .menu-item:focused .label { - -fx-text-fill: #2c2c2c; + -fx-text-fill: white; } From 5a8f52a3d758edf9055ff342336c13b0fc329c78 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 19:25:41 +0300 Subject: [PATCH 08/23] DELETE and SPACE buttons works on TableView level as expected Mouse selection on Tale level re-implemented with pain Multiple selection in Table now possible --- .../Controllers/NSTableViewController.java | 50 +++++++++++++++++-- 1 file changed, 46 insertions(+), 4 deletions(-) diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index 01025d7..ab59bc7 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -13,6 +13,10 @@ import javafx.fxml.Initializable; import javafx.scene.control.*; import javafx.scene.control.cell.CheckBoxTableCell; import javafx.scene.control.cell.PropertyValueFactory; +import javafx.scene.input.KeyCode; +import javafx.scene.input.KeyEvent; +import javafx.scene.input.MouseButton; +import javafx.scene.input.MouseEvent; import javafx.util.Callback; import nsusbloader.MediatorControl; import nsusbloader.NSLDataTypes.EFileStatus; @@ -35,12 +39,40 @@ public class NSTableViewController implements Initializable { rowsObsLst = FXCollections.observableArrayList(); table.setPlaceholder(new Label()); + table.setEditable(false); // At least with hacks it works as expected. Otherwise - null pointer exception + table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY); + table.setOnKeyPressed(new EventHandler() { + @Override + public void handle(KeyEvent keyEvent) { + if (!rowsObsLst.isEmpty()) { + if (keyEvent.getCode() == KeyCode.DELETE) { + rowsObsLst.removeAll(table.getSelectionModel().getSelectedItems()); + if (rowsObsLst.isEmpty()) + MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better + table.refresh(); + } else if (keyEvent.getCode() == KeyCode.SPACE) { + for (NSLRowModel item : table.getSelectionModel().getSelectedItems()) { + item.setMarkForUpload(!item.isMarkForUpload()); + restrictSelection(item); + } + table.refresh(); + } + } + keyEvent.consume(); + } + }); TableColumn statusColumn = new TableColumn<>(resourceBundle.getString("tableStatusLbl")); TableColumn fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl")); TableColumn fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl")); TableColumn uploadColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl")); + + statusColumn.setEditable(false); + fileNameColumn.setEditable(false); + fileSizeColumn.setEditable(false); + uploadColumn.setEditable(true); + // See https://bugs.openjdk.java.net/browse/JDK-8157687 statusColumn.setMinWidth(100.0); statusColumn.setPrefWidth(100.0); @@ -77,7 +109,6 @@ public class NSTableViewController implements Initializable { restrictSelection(model); } }); - return booleanProperty; } }); @@ -101,7 +132,7 @@ public class NSTableViewController implements Initializable { public void handle(ActionEvent actionEvent) { rowsObsLst.remove(row.getItem()); if (rowsObsLst.isEmpty()) - MediatorControl.getInstance().getContoller().disableUploadStopBtn(); + MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better table.refresh(); } }); @@ -110,7 +141,7 @@ public class NSTableViewController implements Initializable { @Override public void handle(ActionEvent actionEvent) { rowsObsLst.clear(); - MediatorControl.getInstance().getContoller().disableUploadStopBtn(); + MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better table.refresh(); } }); @@ -124,6 +155,17 @@ public class NSTableViewController implements Initializable { .then(MediatorControl.getInstance().getTransferActive()?(ContextMenu)null:contextMenu) .otherwise((ContextMenu) null) ); + row.setOnMouseClicked(new EventHandler() { // Just.. don't ask.. + @Override + public void handle(MouseEvent mouseEvent) { + if (!row.isEmpty() && mouseEvent.getButton() == MouseButton.PRIMARY){ + NSLRowModel thisItem = row.getItem(); + thisItem.setMarkForUpload(!thisItem.isMarkForUpload()); + restrictSelection(thisItem); + } + mouseEvent.consume(); + } + }); return row; } } @@ -140,8 +182,8 @@ public class NSTableViewController implements Initializable { if (model != modelChecked) model.setMarkForUpload(false); } - table.refresh(); } + table.refresh(); } /** * Add files when user selected them From b147949c602840fc6abd6564a4a6c0a285c29362 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 21:54:10 +0300 Subject: [PATCH 09/23] Translation to Franch added. Thanks to Stephane Meden! --- README.md | 5 +++++ src/main/resources/res/locale_fra.properties | 23 ++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 src/main/resources/res/locale_fra.properties diff --git a/README.md b/README.md index 3c31438..fe9f864 100644 --- a/README.md +++ b/README.md @@ -73,6 +73,11 @@ Handling successful/failed installation is a purpose of the other side applicati If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it. Upload somewhere (pastebin? google drive? whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add. +### Thanks for great work done by to our translater~~s team~~! + +Français by [Stephane Meden (JackFromNice)](https://github.com/JackFromNice) + + ## TODO: - [x] macOS QA v0.1 (Mojave) - [x] macOS QA v0.2.2 (Mojave) diff --git a/src/main/resources/res/locale_fra.properties b/src/main/resources/res/locale_fra.properties new file mode 100644 index 0000000..753551a --- /dev/null +++ b/src/main/resources/res/locale_fra.properties @@ -0,0 +1,23 @@ +btnFileOpen=Selectionner le fichier .NSP +btnUpload=Envoyer vers NS +logsEnteredAsMsg1=Vous etes connecté en tant que: +logsEnteredAsMsg2=Vous devez être administrateur ou avoir configuré les règles 'udev' pour cet utilisateur afin d'éviter tout problème. +logsFilesToUploadTitle=Fichiers a envoyer: +logsGreetingsMessage=Bienvenue sur NS-USBloader +logsNoFolderFileSelected=Aucuns fichiers sélectionnés: Rien a envoyer. +windowBodyConfirmExit=Le transfert de données est en cours et la fermeture de cette application l’interrompra.\nC’est la pire chose que vous puissiez faire maintenant.\nInterrompre le processus et quitter ? +windowTitleConfirmExit=Non, ne faites pas ça! +btnStop=Interrompre +logsGreetingsMessage2=--\n\ +Source: https://github.com/developersu/ns-usbloader/\n\ +Site: https://developersu.blogspot.com/search/label/NS-USBloader\n\ +Dmitry Isaenko [developer.su] +windowTitleConfirmWrongPFS0=Type de fichier incorrect +windowBodyConfirmWrongPFS0=Le fichier NSP sélectionné a des symboles de départ incorect. Il est très probablement corrompu.\n\ +Il est préférable d'interrompre le processus maintenant. Continuer le processus ? +tableStatusLbl=Statut +tableFileNameLbl=Nom de fichier +tableSizeLbl=Taille +tableUploadLbl=Envoyer ? +contextMenuBtnDelete=Supprimer +contextMenuBtnDeleteAll=Supprimer tout From f747f54f6ca1a0edbf808690b60ba5ce94e06181 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 22:28:56 +0300 Subject: [PATCH 10/23] One more time for French translation --- src/main/resources/locale_fra.properties | 23 ++++++++++++++++++++ src/main/resources/res/locale_fra.properties | 23 -------------------- 2 files changed, 23 insertions(+), 23 deletions(-) create mode 100644 src/main/resources/locale_fra.properties delete mode 100644 src/main/resources/res/locale_fra.properties diff --git a/src/main/resources/locale_fra.properties b/src/main/resources/locale_fra.properties new file mode 100644 index 0000000..08a1964 --- /dev/null +++ b/src/main/resources/locale_fra.properties @@ -0,0 +1,23 @@ +btnFileOpen=Selectionner les fichier .NSP +btnUpload=Envoyer vers NS +logsEnteredAsMsg1=Vous etes connect\u00E9 en tant que: +logsEnteredAsMsg2=Vous devez \u00EAtre administrateur ou avoir configur\u00E9 les r\u00E8gles 'udev' pour cet utilisateur afin d'\u00E9viter tout probl\u00E8me. +logsFilesToUploadTitle=Fichiers a envoyer: +logsGreetingsMessage=Bienvenue sur NS-USBloader +logsNoFolderFileSelected=Aucuns fichiers s\u00E9lectionn\u00E9s: Rien a envoyer. +windowBodyConfirmExit=Le transfert de donn\u00E9es est en cours et la fermeture de cette application l\u2019interrompra.\nC\u2019est la pire chose que vous puissiez faire maintenant.\nInterrompre le processus et quitter ? +windowTitleConfirmExit=Non, ne faites pas \u00E7a! +btnStop=Interrompre +logsGreetingsMessage2=--\n\ +Source: https://github.com/developersu/ns-usbloader/\n\ +Site: https://developersu.blogspot.com/search/label/NS-USBloader\n\ +Dmitry Isaenko [developer.su] +windowTitleConfirmWrongPFS0=Type de fichier incorrect +windowBodyConfirmWrongPFS0=Le fichier NSP s\u00E9lectionn\u00E9 a des symboles de d\u00E9part incorect. Il est tr\u00E8s probablement corrompu.\nIl est pr\u00E9f\u00E9rable d'interrompre le processus maintenant. Continuer le processus ? +tableUploadLbl=Envoyer ? +tableSizeLbl=Taille +tableFileNameLbl=Nom de fichier +tableStatusLbl=Statut +contextMenuBtnDelete=Supprimer +contextMenuBtnDeleteAll=Supprimer tout + diff --git a/src/main/resources/res/locale_fra.properties b/src/main/resources/res/locale_fra.properties deleted file mode 100644 index 753551a..0000000 --- a/src/main/resources/res/locale_fra.properties +++ /dev/null @@ -1,23 +0,0 @@ -btnFileOpen=Selectionner le fichier .NSP -btnUpload=Envoyer vers NS -logsEnteredAsMsg1=Vous etes connecté en tant que: -logsEnteredAsMsg2=Vous devez être administrateur ou avoir configuré les règles 'udev' pour cet utilisateur afin d'éviter tout problème. -logsFilesToUploadTitle=Fichiers a envoyer: -logsGreetingsMessage=Bienvenue sur NS-USBloader -logsNoFolderFileSelected=Aucuns fichiers sélectionnés: Rien a envoyer. -windowBodyConfirmExit=Le transfert de données est en cours et la fermeture de cette application l’interrompra.\nC’est la pire chose que vous puissiez faire maintenant.\nInterrompre le processus et quitter ? -windowTitleConfirmExit=Non, ne faites pas ça! -btnStop=Interrompre -logsGreetingsMessage2=--\n\ -Source: https://github.com/developersu/ns-usbloader/\n\ -Site: https://developersu.blogspot.com/search/label/NS-USBloader\n\ -Dmitry Isaenko [developer.su] -windowTitleConfirmWrongPFS0=Type de fichier incorrect -windowBodyConfirmWrongPFS0=Le fichier NSP sélectionné a des symboles de départ incorect. Il est très probablement corrompu.\n\ -Il est préférable d'interrompre le processus maintenant. Continuer le processus ? -tableStatusLbl=Statut -tableFileNameLbl=Nom de fichier -tableSizeLbl=Taille -tableUploadLbl=Envoyer ? -contextMenuBtnDelete=Supprimer -contextMenuBtnDeleteAll=Supprimer tout From 4da62b4c07d323e7971d61cc34135437cb5b1a70 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Fri, 15 Mar 2019 22:31:32 +0300 Subject: [PATCH 11/23] Last time for French translation --- src/main/resources/locale_fra.properties | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/resources/locale_fra.properties b/src/main/resources/locale_fra.properties index 08a1964..587f665 100644 --- a/src/main/resources/locale_fra.properties +++ b/src/main/resources/locale_fra.properties @@ -1,4 +1,4 @@ -btnFileOpen=Selectionner les fichier .NSP +btnFileOpen=Selectionner les fichiers .NSP btnUpload=Envoyer vers NS logsEnteredAsMsg1=Vous etes connect\u00E9 en tant que: logsEnteredAsMsg2=Vous devez \u00EAtre administrateur ou avoir configur\u00E9 les r\u00E8gles 'udev' pour cet utilisateur afin d'\u00E9viter tout probl\u00E8me. From d41401c85e5873f9a4fbe58cde1bae6e531a29c1 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sat, 16 Mar 2019 02:43:14 +0300 Subject: [PATCH 12/23] Adding files logic for the button has been changed to "add whatever you want to the table without cleanup" --- README.md | 4 +- .../Controllers/NSLMainController.java | 28 +++------- .../Controllers/NSTableViewController.java | 56 ++++++++----------- 3 files changed, 34 insertions(+), 54 deletions(-) diff --git a/README.md b/README.md index fe9f864..0004073 100644 --- a/README.md +++ b/README.md @@ -69,11 +69,11 @@ If you use different MacOS (not Mojave) - check release section for another JAR Table 'Status' = 'Uploaded' does not means that file installed. It means that it has been sent to NS without any issues! That's what this app about. Handling successful/failed installation is a purpose of the other side application (TinFoil/GoldLeaf). (And they don't provide any feedback interfaces so I can't detect success/failure.) -## Translators! Traducteurs! Traductores! Übersetzer! Թարգմանիչներ! +## Translators! Traductores! Übersetzer! Թարգմանիչներ! If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it. Upload somewhere (pastebin? google drive? whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add. -### Thanks for great work done by to our translater~~s team~~! +### Thanks for great work done by our translater~~s team~~! Français by [Stephane Meden (JackFromNice)](https://github.com/JackFromNice) diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index 569a969..f253207 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -128,10 +128,6 @@ public class NSLMainController implements Initializable { uploadStopBtn.setDisable(false); previouslyOpenedPath = filesList.get(0).getParent(); } - else{ - tableFilesListController.setFiles(null); - uploadStopBtn.setDisable(true); - } } /** * It's button listener when no transmission executes @@ -194,8 +190,8 @@ public class NSLMainController implements Initializable { /** * Crunch. Now you see that I'm not a programmer.. This function called from NSTableViewController * */ - public void disableUploadStopBtn(){ - uploadStopBtn.setDisable(true); + public void disableUploadStopBtn(boolean disable){ + uploadStopBtn.setDisable(disable); } /** * Drag-n-drop support (dragOver consumer) @@ -210,6 +206,10 @@ public class NSLMainController implements Initializable { * */ @FXML private void handleDrop(DragEvent event){ + if (MediatorControl.getInstance().getTransferActive()) { + event.setDropCompleted(true); + return; + } List filesDropped = new ArrayList<>(); try { for (File fileOrDir : event.getDragboard().getFiles()) { @@ -224,20 +224,8 @@ public class NSLMainController implements Initializable { catch (SecurityException se){ se.printStackTrace(); } - if (!filesDropped.isEmpty()) { - List filesAlreadyInTable; - if ((filesAlreadyInTable = tableFilesListController.getFiles()) != null) { - filesDropped.removeAll(filesAlreadyInTable); // Get what we already have and add new file(s) - if (!filesDropped.isEmpty()) { - filesDropped.addAll(filesAlreadyInTable); - tableFilesListController.setFiles(filesDropped); - } - } - else { - tableFilesListController.setFiles(filesDropped); - uploadStopBtn.setDisable(false); - } - } + if (!filesDropped.isEmpty()) + tableFilesListController.setFiles(filesDropped); event.setDropCompleted(true); } diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index ab59bc7..6d308aa 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -46,10 +46,10 @@ public class NSTableViewController implements Initializable { @Override public void handle(KeyEvent keyEvent) { if (!rowsObsLst.isEmpty()) { - if (keyEvent.getCode() == KeyCode.DELETE) { + if (keyEvent.getCode() == KeyCode.DELETE && !MediatorControl.getInstance().getTransferActive()) { rowsObsLst.removeAll(table.getSelectionModel().getSelectedItems()); if (rowsObsLst.isEmpty()) - MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better + MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } else if (keyEvent.getCode() == KeyCode.SPACE) { for (NSLRowModel item : table.getSelectionModel().getSelectedItems()) { @@ -132,7 +132,7 @@ public class NSTableViewController implements Initializable { public void handle(ActionEvent actionEvent) { rowsObsLst.remove(row.getItem()); if (rowsObsLst.isEmpty()) - MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better + MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } }); @@ -141,7 +141,7 @@ public class NSTableViewController implements Initializable { @Override public void handle(ActionEvent actionEvent) { rowsObsLst.clear(); - MediatorControl.getInstance().getContoller().disableUploadStopBtn(); // TODO: change to something better + MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // TODO: change to something better table.refresh(); } }); @@ -188,37 +188,29 @@ public class NSTableViewController implements Initializable { /** * Add files when user selected them * */ - public void setFiles(List files){ - rowsObsLst.clear(); // TODO: consider table refresh - if (files == null) - return; - if (protocol.equals("TinFoil")){ - for (File nspFile: files){ - rowsObsLst.add(new NSLRowModel(nspFile, true)); - } + public void setFiles(List newFiles){ + if (!rowsObsLst.isEmpty()){ + List filesAlreayInList = new ArrayList<>(); + for (NSLRowModel model : rowsObsLst) + filesAlreayInList.add(model.getNspFileName()); + for (File file: newFiles) + if (!filesAlreayInList.contains(file.getName())) { + if (protocol.equals("TinFoil")) + rowsObsLst.add(new NSLRowModel(file, true)); + else + rowsObsLst.add(new NSLRowModel(file, false)); + } } else { - rowsObsLst.clear(); - for (File nspFile: files){ - rowsObsLst.add(new NSLRowModel(nspFile, false)); - } - rowsObsLst.get(0).setMarkForUpload(true); + for (File file: newFiles) + if (protocol.equals("TinFoil")) + rowsObsLst.add(new NSLRowModel(file, true)); + else + rowsObsLst.add(new NSLRowModel(file, false)); + MediatorControl.getInstance().getContoller().disableUploadStopBtn(false); } - } - /** - * Return all files no matter how they're marked - * */ - public List getFiles(){ - List files = new ArrayList<>(); - if (rowsObsLst.isEmpty()) - return null; - else - for (NSLRowModel model: rowsObsLst) - files.add(model.getNspFile()); - if (!files.isEmpty()) - return files; - else - return null; + rowsObsLst.get(0).setMarkForUpload(true); + table.refresh(); } /** * Return files ready for upload. Requested from NSLMainController only -> uploadBtnAction() //TODO: set undefined From d55e1c9ba96bbf8bd0ba19e1a169fb447e674b69 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sun, 17 Mar 2019 19:45:51 +0300 Subject: [PATCH 13/23] Intermediate results of net-install support implementation. Front-end 80% ready. Backend 5% ready. --- README.md | 4 +- src/main/java/nsusbloader/AppPreferences.java | 14 +++ .../Controllers/NSLMainController.java | 102 +++++++++++++++--- .../Controllers/NetTabController.java | 49 +++++++++ .../nsusbloader/NET/NETCommunications.java | 62 +++++++++++ src/main/resources/NSLMain.fxml | 13 +++ src/main/resources/NetTab.fxml | 35 ++++++ src/main/resources/locale.properties | 8 ++ src/main/resources/locale_fra.properties | 8 ++ src/main/resources/locale_rus.properties | 8 ++ src/main/resources/locale_ukr.properties | 10 +- src/main/resources/res/app_dark.css | 14 ++- src/main/resources/res/app_light.css | 14 ++- 13 files changed, 321 insertions(+), 20 deletions(-) create mode 100644 src/main/java/nsusbloader/Controllers/NetTabController.java create mode 100644 src/main/java/nsusbloader/NET/NETCommunications.java create mode 100644 src/main/resources/NetTab.fxml diff --git a/README.md b/README.md index 0004073..c6c7759 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # NS-USBloader -NS-USBloader is a PC-side TinFoil and GoldLeaf NSP USB uploader. Replacement for default *usb_install_pc.py* and *GoldTree*. +NS-USBloader is a PC-side TinFoil (USB and Network) and GoldLeaf NSP USB uploader. Replacement for default *usb_install_pc.py*, *remote_install_pc.py* and *GoldTree*. With GUI and cookies. @@ -81,12 +81,14 @@ Français by [Stephane Meden (JackFromNice)](https://github.com/JackFromNice) ## TODO: - [x] macOS QA v0.1 (Mojave) - [x] macOS QA v0.2.2 (Mojave) +- [ ] macOS QA v0.3 (Mojave) - [x] Windows support - [x] code refactoring - [x] GoldLeaf support - [ ] XCI support - [ ] File order sort (non-critical) - [ ] More deep file analyze before uploading. +- [] Network mode support for TinFoil ## Thanks Appreciate assistance and support of both Vitaliy and Konstantin. Without you all this magic would not have happened. diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java index 62baa86..fc5344d 100644 --- a/src/main/java/nsusbloader/AppPreferences.java +++ b/src/main/java/nsusbloader/AppPreferences.java @@ -22,10 +22,24 @@ public class AppPreferences { protocol = "TinFoil"; return protocol; } + public String getNetUsb(){ + String netUsb = preferences.get("NETUSB", "USB"); // Don't let user to change settings manually + if (!netUsb.matches("(^USB$)|(^NET$)")) + netUsb = "USB"; + return netUsb; + } public void setTheme(String theme){ preferences.put("THEME", theme); } public void setProtocol(String protocol){ preferences.put("PROTOCOL", protocol); } + public void setNetUsb(String netUsb){ preferences.put("NETUSB", netUsb); } + public void setNsIp(String ip){preferences.put("NSIP", ip);} + public String getNsIp(){return preferences.get("NSIP", "192.168.1.42");} public String getRecent(){ return preferences.get("RECENT", System.getProperty("user.home")); } public void setRecent(String path){ preferences.put("RECENT", path); } + //------------ SETTINGS ------------------// + public boolean getNsIpValidationNeeded() {return preferences.getBoolean("NSIPVALIDATION", true);} + public void setNsIpValidationNeeded(boolean need){preferences.putBoolean("NSIPVALIDATION", need);} + public boolean getExpertMode(){return preferences.getBoolean("EXPERTMODE", false);} + public void setExpertMode(boolean mode){preferences.putBoolean("EXPERTMODE", mode);} } diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index f253207..5cba9af 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -12,7 +12,9 @@ import javafx.scene.layout.Region; import javafx.stage.FileChooser; import nsusbloader.AppPreferences; import nsusbloader.MediatorControl; +import nsusbloader.NET.NETCommunications; import nsusbloader.NSLMain; +import nsusbloader.ServiceWindow; import nsusbloader.USB.UsbCommunications; import java.io.File; @@ -35,7 +37,7 @@ public class NSLMainController implements Initializable { @FXML public ProgressBar progressBar; // Accessible from Mediator @FXML - private ChoiceBox choiceProtocol; + private ChoiceBox choiceProtocol, choiceNetUsb; @FXML private Button switchThemeBtn; @@ -44,6 +46,12 @@ public class NSLMainController implements Initializable { @FXML public NSTableViewController tableFilesListController; // Accessible from Mediator + @FXML + private NetTabController NetTabController; + @FXML + private TextField nsIpTextField; + @FXML + private Label nsIpLbl; private UsbCommunications usbCommunications; private Thread usbThread; @@ -78,10 +86,46 @@ public class NSLMainController implements Initializable { ObservableList choiceProtocolList = FXCollections.observableArrayList("TinFoil", "GoldLeaf"); choiceProtocol.setItems(choiceProtocolList); - choiceProtocol.getSelectionModel().select(AppPreferences.getInstance().getProtocol()); // TODO: shared settings - choiceProtocol.setOnAction(e->tableFilesListController.setNewProtocol(choiceProtocol.getSelectionModel().getSelectedItem())); // Add listener to notify tableView controller + choiceProtocol.getSelectionModel().select(AppPreferences.getInstance().getProtocol()); + choiceProtocol.setOnAction(e-> { + tableFilesListController.setNewProtocol(choiceProtocol.getSelectionModel().getSelectedItem()); + if (choiceProtocol.getSelectionModel().getSelectedItem().equals("GoldLeaf")) { + choiceNetUsb.setDisable(true); + nsIpLbl.setVisible(false); + nsIpTextField.setVisible(false); + } + else { + choiceNetUsb.setDisable(false); + if (choiceNetUsb.getSelectionModel().getSelectedItem().equals("NET")) { + nsIpLbl.setVisible(true); + nsIpTextField.setVisible(true); + } + } + }); // Add listener to notify tableView controller tableFilesListController.setNewProtocol(choiceProtocol.getSelectionModel().getSelectedItem()); // Notify tableView controller + ObservableList choiceNetUsbList = FXCollections.observableArrayList("USB", "NET"); + choiceNetUsb.setItems(choiceNetUsbList); + choiceNetUsb.getSelectionModel().select(AppPreferences.getInstance().getNetUsb()); + if (choiceProtocol.getSelectionModel().getSelectedItem().equals("GoldLeaf")) { + choiceNetUsb.setDisable(true); + } + choiceNetUsb.setOnAction(e->{ + if (choiceNetUsb.getSelectionModel().getSelectedItem().equals("NET")){ + nsIpLbl.setVisible(true); + nsIpTextField.setVisible(true); + } + else{ + nsIpLbl.setVisible(false); + nsIpTextField.setVisible(false); + } + }); + nsIpTextField.setText(AppPreferences.getInstance().getNsIp()); + if (choiceProtocol.getSelectionModel().getSelectedItem().equals("TinFoil") && choiceNetUsb.getSelectionModel().getSelectedItem().equals("NET")){ + nsIpLbl.setVisible(true); + nsIpTextField.setVisible(true); + } + this.previouslyOpenedPath = null; Region btnSwitchImage = new Region(); @@ -133,20 +177,42 @@ public class NSLMainController implements Initializable { * It's button listener when no transmission executes * */ private void uploadBtnAction(){ - if (usbThread == null || !usbThread.isAlive()){ - List nspToUpload; - if ((nspToUpload = tableFilesListController.getFilesForUpload()) == null) { - logArea.setText(resourceBundle.getString("logsNoFolderFileSelected")); - return; - }else { - logArea.setText(resourceBundle.getString("logsFilesToUploadTitle")+"\n"); - for (File item: nspToUpload) - logArea.appendText(" "+item.getAbsolutePath()+"\n"); + if ((usbThread == null || !usbThread.isAlive())){ + if (choiceProtocol.getSelectionModel().getSelectedItem().equals("GoldLeaf") || + ( + choiceProtocol.getSelectionModel().getSelectedItem().equals("TinFoil") + && choiceNetUsb.getSelectionModel().getSelectedItem().equals("USB") + ) + ){ + List nspToUpload; + if ((nspToUpload = tableFilesListController.getFilesForUpload()) == null) { + logArea.setText(resourceBundle.getString("logsNoFolderFileSelected")); + return; + }else { + logArea.setText(resourceBundle.getString("logsFilesToUploadTitle")+"\n"); + for (File item: nspToUpload) + logArea.appendText(" "+item.getAbsolutePath()+"\n"); + } + usbCommunications = new UsbCommunications(nspToUpload, choiceProtocol.getSelectionModel().getSelectedItem()); + usbThread = new Thread(usbCommunications); + usbThread.setDaemon(true); + usbThread.start(); + } + else { // NET INSTALL OVER TINFOIL + if (NetTabController.isNsIpValidate() && !nsIpTextField.getText().trim().matches("^([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.([01]?\\d\\d?|2[0-4]\\d|25[0-5])$")) + if (!ServiceWindow.getConfirmationWindow(resourceBundle.getString("windowTitleBadIp"),resourceBundle.getString("windowBodyBadIp"))) + return; + String nsIP = nsIpTextField.getText().trim(); + if (!NetTabController.getExpertModeSelected()) { + NETCommunications netCommunications = new NETCommunications(nsIP); + usbThread = new Thread(netCommunications); + usbThread.setDaemon(true); + usbThread.start(); + } + else { + // TODO; pass to another constructor + } } - usbCommunications = new UsbCommunications(nspToUpload, choiceProtocol.getSelectionModel().getSelectedItem()); - usbThread = new Thread(usbCommunications); - usbThread.setDaemon(true); - usbThread.start(); } } /** @@ -235,5 +301,9 @@ public class NSLMainController implements Initializable { public void exit(){ AppPreferences.getInstance().setProtocol(choiceProtocol.getSelectionModel().getSelectedItem()); AppPreferences.getInstance().setRecent(previouslyOpenedPath); + AppPreferences.getInstance().setNetUsb(choiceNetUsb.getSelectionModel().getSelectedItem()); + AppPreferences.getInstance().setNsIp(nsIpTextField.getText().trim()); + AppPreferences.getInstance().setNsIpValidationNeeded(NetTabController.isNsIpValidate()); + AppPreferences.getInstance().setExpertMode(NetTabController.getExpertModeSelected()); } } diff --git a/src/main/java/nsusbloader/Controllers/NetTabController.java b/src/main/java/nsusbloader/Controllers/NetTabController.java new file mode 100644 index 0000000..272ef87 --- /dev/null +++ b/src/main/java/nsusbloader/Controllers/NetTabController.java @@ -0,0 +1,49 @@ +package nsusbloader.Controllers; + +import javafx.fxml.FXML; +import javafx.fxml.Initializable; +import javafx.scene.control.CheckBox; +import javafx.scene.control.Label; +import javafx.scene.control.TextField; +import nsusbloader.AppPreferences; + + +import java.net.URL; +import java.util.ResourceBundle; + +public class NetTabController implements Initializable { + + @FXML + private CheckBox validateNSHostNameCb; + @FXML + private TextField pcIpTextField; + @FXML + private CheckBox expertModeCb; + @FXML + private Label hostIpLbl; + + @Override + public void initialize(URL url, ResourceBundle resourceBundle) { + validateNSHostNameCb.setSelected(AppPreferences.getInstance().getNsIpValidationNeeded()); + if (AppPreferences.getInstance().getExpertMode()) { + expertModeCb.setSelected(true); + hostIpLbl.setVisible(true); + pcIpTextField.setVisible(true); + } + expertModeCb.setOnAction(e->{ + if (expertModeCb.isSelected()){ + hostIpLbl.setVisible(true); + pcIpTextField.setVisible(true); + } + else { + hostIpLbl.setVisible(false); + pcIpTextField.setVisible(false); + } + }); + } + + public boolean getExpertModeSelected(){ + return expertModeCb.isSelected(); + } + public boolean isNsIpValidate(){ return validateNSHostNameCb.isSelected(); } +} diff --git a/src/main/java/nsusbloader/NET/NETCommunications.java b/src/main/java/nsusbloader/NET/NETCommunications.java new file mode 100644 index 0000000..12aa4dd --- /dev/null +++ b/src/main/java/nsusbloader/NET/NETCommunications.java @@ -0,0 +1,62 @@ +package nsusbloader.NET; + +import javafx.concurrent.Task; + +import java.io.*; +import java.net.*; + +public class NETCommunications extends Task { // todo: thows IOException + + private String hostIP; + private String switchIP; + + private ServerSocket serverSocket; + + public NETCommunications(String switchIP){ + this.switchIP = switchIP; + try{ // todo: check other method if internet unavaliable + DatagramSocket socket = new DatagramSocket(); + socket.connect(InetAddress.getByName("8.8.8.8"), 10002); //193.0.14.129 RIPE NCC + hostIP = socket.getLocalAddress().getHostAddress(); + System.out.println(hostIP); + socket.close(); + } + catch (SocketException | UnknownHostException e){ + e.printStackTrace(); + } + + try { + serverSocket = new ServerSocket(6000); // TODO: randomize + //System.out.println(serverSocket.getInetAddress()); 0.0.0.0 + } + catch (IOException ioe){ + ioe.printStackTrace(); + System.out.println("unable to use socket"); + } + } + + @Override + protected Void call() throws Exception { + Socket clientSocket = serverSocket.accept(); + + InputStream is = clientSocket.getInputStream(); + InputStreamReader isr = new InputStreamReader(is); + BufferedReader br = new BufferedReader(isr); + + OutputStream os = clientSocket.getOutputStream(); + OutputStreamWriter osr = new OutputStreamWriter(os); + PrintWriter pw = new PrintWriter(osr); + + String line; + while ((line = br.readLine()) != null) { + if (line.equals("hello world")) { + pw.write("stop doing it!"); + pw.flush(); + break; + } + System.out.println(line); + } + serverSocket.close(); + return null; + } +} diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index 1ce9f82..82a76bd 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -3,10 +3,12 @@ + + @@ -30,6 +32,9 @@ + +