mirror of
https://github.com/developersu/ns-usbloader.git
synced 2025-05-14 15:15:05 -04:00
v0.2
This commit is contained in:
parent
c4d0959cf3
commit
f5a9ddf8df
13 changed files with 162 additions and 91 deletions
23
src/main/java/nsusbloader/AppPreferences.java
Normal file
23
src/main/java/nsusbloader/AppPreferences.java
Normal file
|
@ -0,0 +1,23 @@
|
|||
package nsusbloader;
|
||||
|
||||
import java.util.prefs.Preferences;
|
||||
|
||||
public class AppPreferences {
|
||||
private static final AppPreferences INSTANCE = new AppPreferences();
|
||||
public static AppPreferences getInstance() { return INSTANCE; }
|
||||
|
||||
private Preferences preferences;
|
||||
|
||||
private AppPreferences(){ preferences = Preferences.userRoot().node("NS-USBloader"); }
|
||||
|
||||
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$)"))
|
||||
theme = "/res/app_dark.css";
|
||||
return theme;
|
||||
}
|
||||
public void setTheme(String theme){ preferences.put("THEME", theme); }
|
||||
|
||||
public String getRecent(){ return preferences.get("RECENT", System.getProperty("user.home")); }
|
||||
public void setRecent(String path){ preferences.put("RECENT", path); }
|
||||
}
|
|
@ -1,4 +1,4 @@
|
|||
package nsusbloader;
|
||||
package nsusbloader.Controllers;
|
||||
|
||||
import javafx.collections.FXCollections;
|
||||
import javafx.collections.ObservableList;
|
||||
|
@ -8,7 +8,10 @@ import javafx.scene.control.*;
|
|||
import javafx.scene.layout.Pane;
|
||||
import javafx.scene.layout.Region;
|
||||
import javafx.stage.FileChooser;
|
||||
import nsusbloader.Controllers.NSTableViewController;
|
||||
import nsusbloader.AppPreferences;
|
||||
import nsusbloader.MediatorControl;
|
||||
import nsusbloader.NSLMain;
|
||||
import nsusbloader.UsbCommunications;
|
||||
|
||||
import java.io.File;
|
||||
import java.net.URL;
|
||||
|
@ -46,7 +49,7 @@ public class NSLMainController implements Initializable {
|
|||
@Override
|
||||
public void initialize(URL url, ResourceBundle rb) {
|
||||
this.resourceBundle = rb;
|
||||
logArea.setText(rb.getString("logsGreetingsMessage")+" "+NSLMain.appVersion+"!\n");
|
||||
logArea.setText(rb.getString("logsGreetingsMessage")+" "+ NSLMain.appVersion+"!\n");
|
||||
if (System.getProperty("os.name").toLowerCase().startsWith("lin"))
|
||||
if (!System.getProperty("user.name").equals("root"))
|
||||
logArea.appendText(rb.getString("logsEnteredAsMsg1")+System.getProperty("user.name")+"\n"+rb.getString("logsEnteredAsMsg2") + "\n");
|
||||
|
@ -81,18 +84,20 @@ public class NSLMainController implements Initializable {
|
|||
btnSwitchImage.getStyleClass().add("regionLamp");
|
||||
switchThemeBtn.setGraphic(btnSwitchImage);
|
||||
this.switchThemeBtn.setOnAction(e->switchTheme());
|
||||
|
||||
previouslyOpenedPath = AppPreferences.getInstance().getRecent();
|
||||
}
|
||||
/**
|
||||
* Changes UI theme on the go
|
||||
* */
|
||||
private void switchTheme(){
|
||||
if (switchThemeBtn.getScene().getStylesheets().get(0).equals("/res/app.css")) {
|
||||
switchThemeBtn.getScene().getStylesheets().remove("/res/app.css");
|
||||
if (switchThemeBtn.getScene().getStylesheets().get(0).equals("/res/app_dark.css")) {
|
||||
switchThemeBtn.getScene().getStylesheets().remove("/res/app_dark.css");
|
||||
switchThemeBtn.getScene().getStylesheets().add("/res/app_light.css");
|
||||
}
|
||||
else {
|
||||
switchThemeBtn.getScene().getStylesheets().add("/res/app.css");
|
||||
switchThemeBtn.getScene().getStylesheets().remove("/res/app_light.css");
|
||||
switchThemeBtn.getScene().getStylesheets().add("/res/app_dark.css");
|
||||
}
|
||||
}
|
||||
/**
|
||||
|
@ -103,16 +108,14 @@ public class NSLMainController implements Initializable {
|
|||
List<File> filesList;
|
||||
FileChooser fileChooser = new FileChooser();
|
||||
fileChooser.setTitle(resourceBundle.getString("btnFileOpen"));
|
||||
if (previouslyOpenedPath == null)
|
||||
|
||||
File validator = new File(previouslyOpenedPath);
|
||||
if (validator.exists())
|
||||
fileChooser.setInitialDirectory(validator); // TODO: read from prefs
|
||||
else
|
||||
fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: read from prefs
|
||||
else {
|
||||
File validator = new File(previouslyOpenedPath);
|
||||
if (validator.exists())
|
||||
fileChooser.setInitialDirectory(validator); // TODO: read from prefs
|
||||
else
|
||||
fileChooser.setInitialDirectory(new File(System.getProperty("user.home"))); // TODO: read from prefs
|
||||
}
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NS ROM", "*.nsp"));
|
||||
|
||||
fileChooser.getExtensionFilters().add(new FileChooser.ExtensionFilter("NSP ROM", "*.nsp"));
|
||||
|
||||
filesList = fileChooser.showOpenMultipleDialog(logArea.getScene().getWindow());
|
||||
if (filesList != null && !filesList.isEmpty()) {
|
||||
|
@ -156,7 +159,7 @@ public class NSLMainController implements Initializable {
|
|||
* This thing modify UI for reusing 'Upload to NS' button and make functionality set for "Stop transmission"
|
||||
* Called from mediator
|
||||
* */
|
||||
void notifyTransmissionStarted(boolean isTransmissionStarted){
|
||||
public void notifyTransmissionStarted(boolean isTransmissionStarted){
|
||||
if (isTransmissionStarted) {
|
||||
selectNspBtn.setDisable(true);
|
||||
uploadStopBtn.setOnAction(e->{ stopBtnAction(); });
|
||||
|
@ -182,4 +185,11 @@ public class NSLMainController implements Initializable {
|
|||
uploadStopBtn.getStyleClass().add("buttonUp");
|
||||
}
|
||||
}
|
||||
/**
|
||||
* Save preferences before exit
|
||||
* */
|
||||
public void exit(){
|
||||
AppPreferences.getInstance().setTheme(switchThemeBtn.getScene().getStylesheets().get(0));
|
||||
AppPreferences.getInstance().setRecent(previouslyOpenedPath);
|
||||
}
|
||||
}
|
|
@ -39,14 +39,14 @@ public class NSLRowModel {
|
|||
public void setStatus(EFileStatus status){ // TODO: Localization
|
||||
switch (status){
|
||||
case UPLOADED:
|
||||
this.status = "Uploaded";
|
||||
this.status = "Success";
|
||||
markForUpload = false;
|
||||
break;
|
||||
case FAILED:
|
||||
this.status = "Upload failed";
|
||||
this.status = "Failed";
|
||||
break;
|
||||
case INCORRECT_FILE_FAILED:
|
||||
this.status = "File incorrect";
|
||||
this.status = "Failed: Incorrect file";
|
||||
markForUpload = false;
|
||||
break;
|
||||
}
|
||||
|
|
|
@ -33,15 +33,29 @@ public class NSTableViewController implements Initializable {
|
|||
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||
rowsObsLst = FXCollections.observableArrayList();
|
||||
table.setPlaceholder(new Label());
|
||||
table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
|
||||
|
||||
TableColumn<NSLRowModel, String> statusColumn = new TableColumn<>(resourceBundle.getString("tableStatusLbl"));
|
||||
TableColumn<NSLRowModel, String> fileNameColumn = new TableColumn<>(resourceBundle.getString("tableFileNameLbl"));
|
||||
TableColumn<NSLRowModel, String> fileSizeColumn = new TableColumn<>(resourceBundle.getString("tableSizeLbl"));
|
||||
TableColumn<NSLRowModel, Boolean> uploadColumn = new TableColumn<>(resourceBundle.getString("tableUploadLbl"));
|
||||
statusColumn.setMinWidth(70.0);
|
||||
fileNameColumn.setMinWidth(250.0);
|
||||
fileSizeColumn.setMinWidth(70.0);
|
||||
uploadColumn.setMinWidth(70.0);
|
||||
// See https://bugs.openjdk.java.net/browse/JDK-8157687
|
||||
statusColumn.setMinWidth(100.0);
|
||||
statusColumn.setPrefWidth(100.0);
|
||||
statusColumn.setMaxWidth(100.0);
|
||||
statusColumn.setResizable(false);
|
||||
|
||||
fileNameColumn.setMinWidth(25.0);
|
||||
|
||||
fileSizeColumn.setMinWidth(120.0);
|
||||
fileSizeColumn.setPrefWidth(120.0);
|
||||
fileSizeColumn.setMaxWidth(120.0);
|
||||
fileSizeColumn.setResizable(false);
|
||||
|
||||
uploadColumn.setMinWidth(100.0);
|
||||
uploadColumn.setPrefWidth(100.0);
|
||||
uploadColumn.setMaxWidth(100.0);
|
||||
uploadColumn.setResizable(false);
|
||||
|
||||
statusColumn.setCellValueFactory(new PropertyValueFactory<>("status"));
|
||||
fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("nspFileName"));
|
||||
|
@ -76,12 +90,6 @@ public class NSTableViewController implements Initializable {
|
|||
|
||||
table.setItems(rowsObsLst);
|
||||
table.getColumns().addAll(statusColumn, fileNameColumn, fileSizeColumn, uploadColumn);
|
||||
/* debug content
|
||||
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_2"), true));
|
||||
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_2"), false));
|
||||
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_2"), false));
|
||||
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_2"), true));
|
||||
*/
|
||||
}
|
||||
/**
|
||||
* See uploadColumn callback. In case of GoldLeaf we have to restrict selection
|
||||
|
@ -145,6 +153,7 @@ public class NSTableViewController implements Initializable {
|
|||
model.setStatus(status);
|
||||
}
|
||||
}
|
||||
table.refresh();
|
||||
}
|
||||
/**
|
||||
* Called if selected different USB protocol
|
||||
|
|
|
@ -1,26 +1,28 @@
|
|||
package nsusbloader;
|
||||
|
||||
import nsusbloader.Controllers.NSLMainController;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
class MediatorControl {
|
||||
public class MediatorControl {
|
||||
private AtomicBoolean isTransferActive = new AtomicBoolean(false); // Overcoded just for sure
|
||||
private NSLMainController applicationController;
|
||||
|
||||
static MediatorControl getInstance(){
|
||||
public static MediatorControl getInstance(){
|
||||
return MediatorControlHold.INSTANCE;
|
||||
}
|
||||
|
||||
private static class MediatorControlHold {
|
||||
private static final MediatorControl INSTANCE = new MediatorControl();
|
||||
}
|
||||
void setController(NSLMainController controller){
|
||||
public void setController(NSLMainController controller){
|
||||
this.applicationController = controller;
|
||||
}
|
||||
NSLMainController getContoller(){ return this.applicationController; }
|
||||
|
||||
synchronized void setTransferActive(boolean state) {
|
||||
public synchronized void setTransferActive(boolean state) {
|
||||
isTransferActive.set(state);
|
||||
applicationController.notifyTransmissionStarted(state);
|
||||
}
|
||||
synchronized boolean getTransferActive() { return this.isTransferActive.get(); }
|
||||
public synchronized boolean getTransferActive() { return this.isTransferActive.get(); }
|
||||
}
|
||||
|
|
|
@ -49,19 +49,18 @@ public class MessagesConsumer extends AnimationTimer {
|
|||
if (progressRecieved > 0)
|
||||
progress.forEach(prg -> progressBar.setProgress(prg));
|
||||
|
||||
if (isInterrupted) {
|
||||
if (isInterrupted) { // It's safe 'cuz it's could't be interrupted while HashMap populating
|
||||
MediatorControl.getInstance().setTransferActive(false);
|
||||
progressBar.setProgress(0.0);
|
||||
|
||||
if (statusMap.size() > 0) // It's safe 'cuz it's could't be interrupted while HashMap populating
|
||||
if (statusMap.size() > 0)
|
||||
for (String key : statusMap.keySet())
|
||||
tableViewController.setFileStatus(key, statusMap.get(key));
|
||||
this.stop();
|
||||
}
|
||||
//TODO
|
||||
}
|
||||
|
||||
void interrupt(){
|
||||
this.isInterrupted = true;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -6,22 +6,24 @@ import javafx.scene.Parent;
|
|||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import nsusbloader.Controllers.NSLMainController;
|
||||
|
||||
import java.util.Locale;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
public class NSLMain extends Application {
|
||||
static final String appVersion = "v0.2-DEVELOPMENT";
|
||||
public static final String appVersion = "v0.2";
|
||||
@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"));
|
||||
|
||||
FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml"));
|
||||
|
||||
loader.setResources(rb);
|
||||
Parent root = loader.load();
|
||||
|
||||
|
@ -36,7 +38,9 @@ public class NSLMain extends Application {
|
|||
primaryStage.setMinWidth(600);
|
||||
primaryStage.setMinHeight(375);
|
||||
Scene mainScene = new Scene(root, 800, 400);
|
||||
mainScene.getStylesheets().add("/res/app.css");
|
||||
|
||||
mainScene.getStylesheets().add(AppPreferences.getInstance().getTheme());
|
||||
|
||||
primaryStage.setScene(mainScene);
|
||||
primaryStage.show();
|
||||
|
||||
|
@ -45,6 +49,9 @@ public class NSLMain extends Application {
|
|||
if(! ServiceWindow.getConfirmationWindow(rb.getString("windowTitleConfirmExit"), rb.getString("windowBodyConfirmExit")))
|
||||
e.consume();
|
||||
});
|
||||
|
||||
NSLMainController controller = loader.getController();
|
||||
primaryStage.setOnHidden(e-> controller.exit());
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
|
|
@ -35,12 +35,9 @@ public class ServiceWindow {
|
|||
alertBox.getDialogPane().setMinWidth(Region.USE_PREF_SIZE);
|
||||
alertBox.getDialogPane().setMinHeight(Region.USE_PREF_SIZE);
|
||||
alertBox.setResizable(true); // Java bug workaround for JDR11/OpenJFX. TODO: nothing. really.
|
||||
alertBox.getDialogPane().getStylesheets().add("/res/app.css");
|
||||
alertBox.getDialogPane().getStylesheets().add(AppPreferences.getInstance().getTheme());
|
||||
Optional<ButtonType> result = alertBox.showAndWait();
|
||||
if (result.get() == ButtonType.OK)
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
|
||||
return (result.isPresent() && result.get() == ButtonType.OK);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,12 +18,13 @@ import java.util.List;
|
|||
import java.util.concurrent.BlockingQueue;
|
||||
import java.util.concurrent.LinkedBlockingQueue;
|
||||
|
||||
class UsbCommunications extends Task<Void> {
|
||||
public class UsbCommunications extends Task<Void> {
|
||||
private final int DEFAULT_INTERFACE = 0;
|
||||
|
||||
private BlockingQueue<String> msgQueue;
|
||||
private BlockingQueue<Double> progressQueue;
|
||||
private HashMap<String, EFileStatus> statusMap; // BlockingQueue for literally one object. TODO: read more books ; replace to hashMap
|
||||
private EFileStatus status = EFileStatus.FAILED;
|
||||
|
||||
private MessagesConsumer msgConsumer;
|
||||
|
||||
|
@ -44,7 +45,7 @@ class UsbCommunications extends Task<Void> {
|
|||
Since this application let user an ability (theoretically) to choose same files in different folders, the latest selected file will be added to the list and handled correctly.
|
||||
I have no idea why he/she will make a decision to do that. Just in case, we're good in this point.
|
||||
*/
|
||||
UsbCommunications(List<File> nspList, String protocol){
|
||||
public UsbCommunications(List<File> nspList, String protocol){
|
||||
this.protocol = protocol;
|
||||
this.nspMap = new HashMap<>();
|
||||
for (File f: nspList)
|
||||
|
@ -275,28 +276,17 @@ class UsbCommunications extends Task<Void> {
|
|||
printLog("\tEnd chain", EMsgType.INFO);
|
||||
return null;
|
||||
}
|
||||
/**
|
||||
* Report transfer status
|
||||
* */
|
||||
private void reportTransferStatus(EFileStatus status){
|
||||
for (String fileName: nspMap.keySet())
|
||||
statusMap.put(fileName, status);
|
||||
}
|
||||
/**
|
||||
* Tinfoil processing
|
||||
* */
|
||||
private class TinFoil{
|
||||
TinFoil(){
|
||||
|
||||
if (!sendListOfNSP()) {
|
||||
reportTransferStatus(EFileStatus.FAILED);
|
||||
if (!sendListOfNSP())
|
||||
return;
|
||||
}
|
||||
|
||||
if (proceedCommands()) // REPORT SUCCESS
|
||||
reportTransferStatus(EFileStatus.UPLOADED);
|
||||
else // REPORT FAILURE
|
||||
reportTransferStatus(EFileStatus.FAILED);
|
||||
status = EFileStatus.UPLOADED; // Don't change status that is already set to FAILED
|
||||
}
|
||||
/**
|
||||
* Send what NSP will be transferred
|
||||
|
@ -547,15 +537,14 @@ class UsbCommunications extends Task<Void> {
|
|||
PFSProvider pfsElement = new PFSProvider(nspMap.get(nspMap.keySet().toArray()[0]), msgQueue);
|
||||
if (!pfsElement.init()) {
|
||||
printLog("GL File provided have incorrect structure and won't be uploaded", EMsgType.FAIL);
|
||||
reportTransferStatus(EFileStatus.INCORRECT_FILE_FAILED);
|
||||
status = EFileStatus.INCORRECT_FILE_FAILED;
|
||||
return;
|
||||
}
|
||||
printLog("GL File structure validated and it will be uploaded", EMsgType.PASS);
|
||||
|
||||
if (initGoldLeafProtocol(pfsElement))
|
||||
reportTransferStatus(EFileStatus.UPLOADED);
|
||||
else
|
||||
reportTransferStatus(EFileStatus.FAILED);
|
||||
status = EFileStatus.UPLOADED;
|
||||
// else - no change status that is already set to FAILED
|
||||
}
|
||||
private boolean initGoldLeafProtocol(PFSProvider pfsElement){
|
||||
// Go parse commands
|
||||
|
@ -774,6 +763,11 @@ class UsbCommunications extends Task<Void> {
|
|||
LibUsb.exit(contextNS);
|
||||
printLog("Requested context close", EMsgType.INFO);
|
||||
}
|
||||
|
||||
// Report status
|
||||
for (String fileName: nspMap.keySet())
|
||||
statusMap.put(fileName, status);
|
||||
|
||||
msgConsumer.interrupt();
|
||||
}
|
||||
/**
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue