diff --git a/README.md b/README.md
index eeddcb1..bfb9024 100644
--- a/README.md
+++ b/README.md
@@ -1,83 +1,130 @@
# 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 (USB) NSP installer. Replacement for default **usb_install_pc.py**, **remote_install_pc.py** *(never ever use this. even if you brave. no idea why it works.)* and **GoldTree**.
-With GUI and cookies.
+With GUI and cookies. Wokrs on Windows, macOS and Linux.
-Read more: https://developersu.blogspot.com/2019/02/ns-usbloader-en.html
-
-Here is the version of 'not perfect but anyway' [tinfoil I use](https://cloud.mail.ru/public/DwbX/H8d2p3aYR).
-Ok, I'm almost sure that this version has bugs. I don't remember where I downloaded it. But it works for me somehow..
-
-Let's rephrase, if you have working version of TinFoil DO NOT use this one.
+Sometimes I add new posts [on my home page](https://developersu.blogspot.com/search/label/NS-USBloader) about this project.

-## License
+### License
Source code spreads under the GNU General Public License v.3. You can find it in LICENSE file.
-## Used libraries
+### Used libraries
* [OpenJFX](https://wiki.openjdk.java.net/display/OpenJFX/Main)
* [usb4java](https://mvnrepository.com/artifact/org.usb4java/usb4java)
* Few icons taken from: [materialdesignicons](http://materialdesignicons.com/)
-## Requirements
+### System requirements
-JRE 8u60 or higher. See below.
+JRE 8u60 or higher.
-## Usage
-### Linux:
+### Usage
+#### How to start it on..
+##### Linux:
1. Install JRE/JDK 8u60 or higher (openJDK is good. Oracle's one is also good). JavaFX not needed, if you're interested (it's embedded).
2. `root # java -jar /path/to/NS-USBloader.jar`
-### macOS
+##### macOS
-See 'Linux' section.
+Double-click on downloaded .jar file. Follow instructions. Or see 'Linux' section.
Set 'Security & Privacy' settings if needed.
-### Windows:
+If you use different MacOS (not Mojave) - check release section for another JAR file.
-* Download Zadig: https://zadig.akeo.ie/
-* Open tinfoil. Set 'Title Managment' -> 'Usb install NSP'
+##### Windows:
+
+* Download Zadig: [https://zadig.akeo.ie/](https://zadig.akeo.ie/)
+* 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`)
* Remember to have fun!
-## Tips&tricks
-### Linux: Add user to udev rules to use NS not-from-root-account
-`root # vim /etc/udev/rules.d/99-NS.rules`
+#### And how to use it?
-`SUBSYSTEM=="usb", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="3000", GROUP="plugdev"`
+The first thing you should do it install TinFoil ([Adubbz](https://github.com/Adubbz/Tinfoil/)) or GoldLeaf ([XorTroll](https://github.com/XorTroll/Goldleaf)) on your NS. I recommend using TinFoil, but it ups to you. Take a look on app, find where is the option to install from USB and/or Network. Maybe [this article](https://developersu.blogspot.com/2019/02/ns-usbloader-en.html) will be helpful.
-`root # udevadm control --reload-rules && udevadm trigger`
+Here is the version of 'not perfect but anyway' [tinfoil I use](https://cloud.mail.ru/public/DwbX/H8d2p3aYR).
+Ok, I'm almost sure that this version has bugs. I don't remember where I downloaded it. But it works for me somehow.
-## Known bugs
-* Unable to interrupt transmission when libusb awaiting for read event (when user sent NSP list but didn't selected anything on NS).
+Let's rephrase, if you have working version of TinFoil **DO NOT** use this one. Ok. let's begin.
-## NOTES
-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.
+There are three tabs. Firs one is main.
+
+##### First tab.
+
+At the top of you selecting from drop-down application and protocol that you're going to use. For GoldLeaf only USB is available. Lamp icon stands for switching themes (light or dark).
+
+Then you may drag-n-drop folder with NSPs OR files to application or use 'Select NSP files' button. Multiple selection for files available. Click it again and select files from another folder it you want, it will be added into the table.
+
+Table.
+
+There you can select checkbox for files that will be send to application (TF/GL). Since GoldLeaf allow you only one file transmission per time, only one file is available for selection. Also you can use space to select/un-select files and 'delete' button for deleting. By right-mouse-click you can see context menu where you can delete one OR all items from the table.
+
+##### Second tab.
+
+Here you can configure settings for network file transmission. Usually you shouldn't change anything. But it you're cool hacker, go ahead! The most interesting option here is 'Don't serve requests'. Architecture of the TinFoil networking is working interesting way. When you select in TF network NSP transfer, application will wait at port 2000 for the information about where should it take files from. Like '192.168.1.5:6060/my_file.nsp'. Usually NS-USBloader serves requests by implementing simplified HTTP server and bringing it up and so on. But if this option selected, you can define path to remote location of the files. For example if you set in settings 'shared.lan:80/ROMS/NS/' and add in table file 'my file.nsp' then NS-USBloader will simply tell TinFoil "Hey, go take files from 'shared.lan:80/ROMS/NS/my+file.nsp' ". Of course you have to bring 'shared.lan' host up and make file accessible from such address. All this requires more investigation. BTW, the issue could be that NS-USBloader encodes 'space' char as '+' and some web-servers understand 'space' as '%20D'. It could be fixed in later versions of NS-USBloader if I go deeper in it or you leave me feedback with information/request. As I said, this feature is interesting, but I guess won't be popular.
+
+##### Third tab.
+
+That's where all logs dropped. Verbose information about transmissions comes here.
+
+Why when 'Net' once started it never ends?
+
+Because there is HTTP server inside of application. It can't determine the moment when all transmissions finished (unless they failed). So you have to look on your NS screen and 'Interrupt' is once done.
+
+### Tips&tricks
+#### Linux: Add user to 'udev' rules to use NS not-from-root-account
+```
+root # vim /etc/udev/rules.d/99-NS.rules
+SUBSYSTEM=="usb", ATTRS{idVendor}=="057e", ATTRS{idProduct}=="3000", GROUP="plugdev"
+root # udevadm control --reload-rules && udevadm trigger
+```
+
+### Known bugs
+* Unable to interrupt transmission when libusb awaiting for read event (when user sent NSP list but didn't selected anything on NS). Also, sometimes, when network transmission started and nothing received from NS.
+
+#### NOTES
+Table 'Status' = 'Uploaded' does not mean 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.)
-## TODO:
-- [x] macOS QA v0.1
-- [ ] macOS QA v0.2 (partly)
+usb4java since NS-USBloader-v0.2.3 switched to 1.2.0 instead of 1.3.0. This shouldn't impact anyone except users of macOS High Sierra (and Sierra?) where previous versions of NS-USBloader didn't work.
+
+### 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 our translater~~s team~~!
+
+Français by [Stephane Meden (JackFromNice)](https://github.com/JackFromNice)
+
+
+#### TODO (maybe):
+- [x] macOS QA v0.1 (Mojave)
+- [x] macOS QA v0.2.2 (Mojave)
+- [x] macOS QA v0.2.3-DEV (High Sierra)
+- [ ] macOS QA v0.3 (Mojave, High Sierra)
- [x] Windows support
-- [ ] code refactoring (almost. todo: printLog() )
+- [x] code refactoring
- [x] GoldLeaf support
- [ ] XCI support
- [ ] File order sort (non-critical)
- [ ] More deep file analyze before uploading.
+- [x] Network mode support for TinFoil
-## Thanks
+#### Thanks
Appreciate assistance and support of both Vitaliy and Konstantin. Without you all this magic would not have happened.
[Konstanin Kelemen](https://github.com/konstantin-kelemen)
-
-[Vitaliy Natarov](https://github.com/SebastianUA)
\ No newline at end of file
+
+[Vitaliy Natarov](https://github.com/SebastianUA)
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 5665658..30bc6e0 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,11 +8,11 @@
NS-USBloaderns-usbloader
- 0.2.2-SNAPSHOT
+ 0.3-SNAPSHOThttps://github.com/developersu/ns-usbloader/
- NSP USB loader for TinFoil and GoldLeaf
+ NSP USB loader for TinFoil (USB and Network) and GoldLeaf
2019
@@ -140,7 +140,7 @@
org.usb4javausb4java
- 1.3.0
+ 1.2.0compile
@@ -155,6 +155,18 @@
1.8
+
+
+ maven-jar-plugin
+ 2.4
+
+
+ default-jar
+ none
+
+
+
+
org.apache.maven.pluginsmaven-assembly-plugin
diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java
index 62baa86..83d03b1 100644
--- a/src/main/java/nsusbloader/AppPreferences.java
+++ b/src/main/java/nsusbloader/AppPreferences.java
@@ -10,6 +10,33 @@ public class AppPreferences {
private AppPreferences(){ preferences = Preferences.userRoot().node("NS-USBloader"); }
+ public void setAll(
+ String Protocol,
+ String PreviouslyOpened,
+ String NetUsb,
+ String NsIp,
+ boolean NsIpValidate,
+ boolean ExpertMode,
+ boolean AutoIp,
+ boolean RandPort,
+ boolean NotServe,
+ String HostIp,
+ String HostPort,
+ String HostExtra
+ ){
+ setProtocol(Protocol);
+ setRecent(PreviouslyOpened);
+ setNetUsb(NetUsb);
+ setNsIp(NsIp);
+ setNsIpValidationNeeded(NsIpValidate);
+ setExpertMode(ExpertMode);
+ setAutoDetectIp(AutoIp);
+ setRandPort(RandPort);
+ setNotServeRequests(NotServe);
+ setHostIp(HostIp);
+ setHostPort(HostPort);
+ setHostExtra(HostExtra);
+ }
public String getTheme(){
String theme = preferences.get("THEME", "/res/app_dark.css"); // Don't let user to change settings manually
if (!theme.matches("(^/res/app_dark.css$)|(^/res/app_light.css$)"))
@@ -22,10 +49,50 @@ 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);}
+
+ public boolean getAutoDetectIp(){return preferences.getBoolean("AUTOHOSTIP", true);}
+ public void setAutoDetectIp(boolean mode){preferences.putBoolean("AUTOHOSTIP", mode);}
+
+ public boolean getRandPort(){return preferences.getBoolean("RANDHOSTPORT", true);}
+ public void setRandPort(boolean mode){preferences.putBoolean("RANDHOSTPORT", mode);}
+
+ public boolean getNotServeRequests(){return preferences.getBoolean("DONTSERVEREQ", false);}
+ public void setNotServeRequests(boolean mode){preferences.putBoolean("DONTSERVEREQ", mode);}
+
+ public String getHostIp(){ return preferences.get("HOSTIP", "0.0.0.0").replaceAll("(\\s)|(\t)", "");} // who the hell said 'paranoid'?
+ public void setHostIp(String ip){preferences.put("HOSTIP", ip);}
+
+ public String getHostPort(){
+ String value = preferences.get("HOSTPORT", "6042");
+ if (!value.matches("^[0-9]{1,5}$"))
+ return "6042";
+ if ((Integer.parseInt(value) > 65535) || (Integer.parseInt(value) < 1))
+ return "6042";
+ return value;
+ }
+ public void setHostPort(String port){preferences.put("HOSTPORT", port);}
+
+ public String getHostExtra(){ return preferences.get("HOSTEXTRA", "").replaceAll("(\\s)|(\t)", "");} // oh just shut up...
+ public void setHostExtra(String postfix){preferences.put("HOSTEXTRA", postfix);}
}
diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java
index 3befcbd..9787119 100644
--- a/src/main/java/nsusbloader/Controllers/NSLMainController.java
+++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java
@@ -2,19 +2,25 @@ package nsusbloader.Controllers;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
+import javafx.concurrent.Task;
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;
import nsusbloader.AppPreferences;
import nsusbloader.MediatorControl;
+import nsusbloader.NET.NETCommunications;
import nsusbloader.NSLMain;
-import nsusbloader.UsbCommunications;
+import nsusbloader.ServiceWindow;
+import nsusbloader.USB.UsbCommunications;
import java.io.File;
-import java.net.URL;
+import java.net.*;
+import java.util.ArrayList;
import java.util.List;
import java.util.ResourceBundle;
@@ -32,7 +38,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;
@@ -41,9 +47,15 @@ public class NSLMainController implements Initializable {
@FXML
public NSTableViewController tableFilesListController; // Accessible from Mediator
+ @FXML
+ private SettingsController SettingsTabController;
+ @FXML
+ private TextField nsIpTextField;
+ @FXML
+ private Label nsIpLbl;
- private UsbCommunications usbCommunications;
- private Thread usbThread;
+ private Task usbNetCommunications;
+ private Thread workThread;
private String previouslyOpenedPath;
@@ -75,10 +87,51 @@ 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);
+ }
+ nsIpTextField.setTextFormatter(new TextFormatter<>(change -> {
+ if (change.getControlNewText().contains(" ") | change.getControlNewText().contains("\t"))
+ return null;
+ else
+ return change;
+ }));
this.previouslyOpenedPath = null;
Region btnSwitchImage = new Region();
@@ -125,37 +178,67 @@ 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
* */
private void uploadBtnAction(){
- if (usbThread == null || !usbThread.isAlive()){
+ if ((workThread == null || !workThread.isAlive())){
+ // Collect files
List nspToUpload;
- if ((nspToUpload = tableFilesListController.getFiles()) == null) {
- resourceBundle.getString("logsNoFolderFileSelected");
+ if ((nspToUpload = tableFilesListController.getFilesForUpload()) == null) {
+ logArea.setText(resourceBundle.getString("logsNoFolderFileSelected"));
return;
- }else {
+ }
+ 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();
+ // If USB selected
+ if (choiceProtocol.getSelectionModel().getSelectedItem().equals("GoldLeaf") ||
+ (
+ choiceProtocol.getSelectionModel().getSelectedItem().equals("TinFoil")
+ && choiceNetUsb.getSelectionModel().getSelectedItem().equals("USB")
+ )
+ ){
+ usbNetCommunications = new UsbCommunications(nspToUpload, choiceProtocol.getSelectionModel().getSelectedItem());
+ workThread = new Thread(usbNetCommunications);
+ workThread.setDaemon(true);
+ workThread.start();
+ }
+ else { // NET INSTALL OVER TINFOIL
+ if (SettingsTabController.isNsIpValidate() && !nsIpTextField.getText().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();
+
+ if (!SettingsTabController.getExpertModeSelected())
+ usbNetCommunications = new NETCommunications(nspToUpload, nsIP, false, "", "", "");
+ else {
+ usbNetCommunications = new NETCommunications(
+ nspToUpload,
+ nsIP,
+ SettingsTabController.getNotServeSelected(),
+ SettingsTabController.getAutoIpSelected()?"":SettingsTabController.getHostIp(),
+ SettingsTabController.getRandPortSelected()?"":SettingsTabController.getHostPort(),
+ SettingsTabController.getNotServeSelected()?SettingsTabController.getHostExtra():""
+ );
+ }
+
+ workThread = new Thread(usbNetCommunications);
+ workThread.setDaemon(true);
+ workThread.start();
+ }
}
}
/**
* It's button listener when transmission in progress
* */
private void stopBtnAction(){
- if (usbThread != null && usbThread.isAlive()){
- usbCommunications.cancel(false);
+ if (workThread != null && workThread.isAlive()){
+ usbNetCommunications.cancel(false); // TODO: add something abstract to use also for network
}
}
/**
@@ -188,11 +271,65 @@ 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(boolean disable){
+ uploadStopBtn.setDisable(disable);
+ }
+ /**
+ * 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){
+ if (MediatorControl.getInstance().getTransferActive()) {
+ event.setDropCompleted(true);
+ return;
+ }
+ 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())
+ tableFilesListController.setFiles(filesDropped);
+
+ event.setDropCompleted(true);
+ }
/**
* Save preferences before exit
* */
- public void exit(){
- AppPreferences.getInstance().setProtocol(choiceProtocol.getSelectionModel().getSelectedItem());
- AppPreferences.getInstance().setRecent(previouslyOpenedPath);
+ public void exit(){ // TODO: add method to set all in AppPreferences
+ AppPreferences.getInstance().setAll(
+ choiceProtocol.getSelectionModel().getSelectedItem(),
+ previouslyOpenedPath,
+ choiceNetUsb.getSelectionModel().getSelectedItem(),
+ nsIpTextField.getText().trim(),
+ SettingsTabController.isNsIpValidate(),
+ SettingsTabController.getExpertModeSelected(),
+ SettingsTabController.getAutoIpSelected(),
+ SettingsTabController.getRandPortSelected(),
+ SettingsTabController.getNotServeSelected(),
+ SettingsTabController.getHostIp(),
+ SettingsTabController.getHostPort(),
+ SettingsTabController.getHostExtra()
+ );
}
}
diff --git a/src/main/java/nsusbloader/Controllers/NSLRowModel.java b/src/main/java/nsusbloader/Controllers/NSLRowModel.java
index afd3f17..baa592e 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
@@ -45,11 +50,14 @@ public class NSLRowModel {
case FAILED:
this.status = "Failed";
break;
- case UNKNOWN:
+ case INDETERMINATE:
this.status = "...";
break;
+ case UNKNOWN:
+ this.status = "Unknown";
+ break;
case INCORRECT_FILE_FAILED:
- this.status = "Failed: Incorrect file";
+ this.status = "Failed: Bad file";
markForUpload = false;
break;
}
diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java
index 9e1670c..ba20ffa 100644
--- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java
+++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java
@@ -1,19 +1,24 @@
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.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.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;
import java.io.File;
@@ -32,13 +37,42 @@ public class NSTableViewController implements Initializable {
@Override
public void initialize(URL url, ResourceBundle resourceBundle) {
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 && !MediatorControl.getInstance().getTransferActive()) {
+ rowsObsLst.removeAll(table.getSelectionModel().getSelectedItems());
+ if (rowsObsLst.isEmpty())
+ MediatorControl.getInstance().getContoller().disableUploadStopBtn(true); // 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);
@@ -75,7 +109,6 @@ public class NSTableViewController implements Initializable {
restrictSelection(model);
}
});
-
return booleanProperty;
}
});
@@ -87,7 +120,56 @@ 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(true); // TODO: change to something better
+ 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(true); // TODO: change to something better
+ 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)
+ );
+ 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;
+ }
+ }
+ );
table.setItems(rowsObsLst);
table.getColumns().addAll(statusColumn, fileNameColumn, fileSizeColumn, uploadColumn);
}
@@ -100,36 +182,42 @@ public class NSTableViewController implements Initializable {
if (model != modelChecked)
model.setMarkForUpload(false);
}
- table.refresh();
}
+ table.refresh();
}
/**
* 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);
}
+ rowsObsLst.get(0).setMarkForUpload(true);
+ table.refresh();
}
/**
* 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;
@@ -137,7 +225,7 @@ public class NSTableViewController implements Initializable {
for (NSLRowModel model: rowsObsLst){
if (model.isMarkForUpload()){
files.add(model.getNspFile());
- model.setStatus(EFileStatus.UNKNOWN);
+ model.setStatus(EFileStatus.INDETERMINATE);
}
}
if (!files.isEmpty()) {
@@ -178,4 +266,4 @@ public class NSTableViewController implements Initializable {
table.refresh();
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/nsusbloader/Controllers/SettingsController.java b/src/main/java/nsusbloader/Controllers/SettingsController.java
new file mode 100644
index 0000000..8289f49
--- /dev/null
+++ b/src/main/java/nsusbloader/Controllers/SettingsController.java
@@ -0,0 +1,146 @@
+package nsusbloader.Controllers;
+
+import javafx.fxml.FXML;
+import javafx.fxml.Initializable;
+import javafx.scene.control.CheckBox;
+import javafx.scene.control.TextField;
+import javafx.scene.control.TextFormatter;
+import javafx.scene.layout.VBox;
+import nsusbloader.AppPreferences;
+import nsusbloader.ServiceWindow;
+
+import java.net.URL;
+import java.util.ResourceBundle;
+
+public class SettingsController implements Initializable {
+
+ @FXML
+ private CheckBox validateNSHostNameCb;
+ @FXML
+ private CheckBox expertModeCb;
+ @FXML
+ private CheckBox autoDetectIpCb;
+ @FXML
+ private CheckBox randPortCb;
+
+ @FXML
+ private TextField pcIpTextField;
+ @FXML
+ private TextField pcPortTextField;
+ @FXML
+ private TextField pcExtraTextField;
+
+ @FXML
+ private CheckBox dontServeCb;
+
+ @FXML
+ private VBox expertSettingsVBox;
+
+ @Override
+ public void initialize(URL url, ResourceBundle resourceBundle) {
+ validateNSHostNameCb.setSelected(AppPreferences.getInstance().getNsIpValidationNeeded());
+
+ expertSettingsVBox.setDisable(!AppPreferences.getInstance().getExpertMode());
+
+ expertModeCb.setSelected(AppPreferences.getInstance().getExpertMode());
+ expertModeCb.setOnAction(e->{
+ expertSettingsVBox.setDisable(!expertModeCb.isSelected());
+ });
+
+ autoDetectIpCb.setSelected(AppPreferences.getInstance().getAutoDetectIp());
+ pcIpTextField.setDisable(AppPreferences.getInstance().getAutoDetectIp());
+ autoDetectIpCb.setOnAction(e->{
+ pcIpTextField.setDisable(autoDetectIpCb.isSelected());
+ if (!autoDetectIpCb.isSelected())
+ pcIpTextField.requestFocus();
+ });
+
+ randPortCb.setSelected(AppPreferences.getInstance().getRandPort());
+ pcPortTextField.setDisable(AppPreferences.getInstance().getRandPort());
+ randPortCb.setOnAction(e->{
+ pcPortTextField.setDisable(randPortCb.isSelected());
+ if (!randPortCb.isSelected())
+ pcPortTextField.requestFocus();
+ });
+
+ if (AppPreferences.getInstance().getNotServeRequests()){
+ dontServeCb.setSelected(true);
+
+ autoDetectIpCb.setSelected(false);
+ autoDetectIpCb.setDisable(true);
+ pcIpTextField.setDisable(false);
+
+ randPortCb.setSelected(false);
+ randPortCb.setDisable(true);
+ pcPortTextField.setDisable(false);
+ }
+ pcExtraTextField.setDisable(!AppPreferences.getInstance().getNotServeRequests());
+
+ dontServeCb.setOnAction(e->{
+ if (dontServeCb.isSelected()){
+ autoDetectIpCb.setSelected(false);
+ autoDetectIpCb.setDisable(true);
+ pcIpTextField.setDisable(false);
+
+ randPortCb.setSelected(false);
+ randPortCb.setDisable(true);
+ pcPortTextField.setDisable(false);
+
+ pcExtraTextField.setDisable(false);
+ pcIpTextField.requestFocus();
+ }
+ else {
+ autoDetectIpCb.setDisable(false);
+ autoDetectIpCb.setSelected(true);
+ pcIpTextField.setDisable(true);
+
+ randPortCb.setDisable(false);
+ randPortCb.setSelected(true);
+ pcPortTextField.setDisable(true);
+
+ pcExtraTextField.setDisable(true);
+ }
+ });
+
+ pcIpTextField.setText(AppPreferences.getInstance().getHostIp());
+ pcPortTextField.setText(AppPreferences.getInstance().getHostPort());
+ pcExtraTextField.setText(AppPreferences.getInstance().getHostExtra());
+
+ pcIpTextField.setTextFormatter(new TextFormatter<>(change -> {
+ if (change.getControlNewText().contains(" ") | change.getControlNewText().contains("\t"))
+ return null;
+ else
+ return change;
+ }));
+ pcPortTextField.setTextFormatter(new TextFormatter