v0.2-development intermediate results of friday codings
This commit is contained in:
parent
f7e2ae9771
commit
49bd078bc5
11 changed files with 684 additions and 339 deletions
55
src/main/java/nsusbloader/Controllers/NSLRowModel.java
Normal file
55
src/main/java/nsusbloader/Controllers/NSLRowModel.java
Normal file
|
@ -0,0 +1,55 @@
|
||||||
|
package nsusbloader.Controllers;
|
||||||
|
|
||||||
|
import nsusbloader.NSLDataTypes.FileStatus;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
|
||||||
|
public class NSLRowModel {
|
||||||
|
|
||||||
|
private String status; // 0 = unknown, 1 = uploaded, 2 = bad file
|
||||||
|
private File nspFile;
|
||||||
|
private String nspFileName;
|
||||||
|
private boolean markForUpload;
|
||||||
|
|
||||||
|
NSLRowModel(File nspFile, boolean checkBoxValue){
|
||||||
|
this.nspFile = nspFile;
|
||||||
|
this.markForUpload = checkBoxValue;
|
||||||
|
this.nspFileName = nspFile.getName();
|
||||||
|
this.status = "";
|
||||||
|
}
|
||||||
|
// Model methods start
|
||||||
|
public String getStatus(){
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
public String getNspFileName(){
|
||||||
|
return nspFileName;
|
||||||
|
}
|
||||||
|
public boolean isMarkForUpload() {
|
||||||
|
return markForUpload;
|
||||||
|
}
|
||||||
|
// Model methods end
|
||||||
|
|
||||||
|
public void setMarkForUpload(boolean value){
|
||||||
|
markForUpload = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setStatus(FileStatus status){ // TODO: Localization
|
||||||
|
switch (status){
|
||||||
|
case FAILED:
|
||||||
|
this.status = "Upload failed";
|
||||||
|
break;
|
||||||
|
case UPLOADED:
|
||||||
|
this.status = "Uploaded";
|
||||||
|
markForUpload = false;
|
||||||
|
break;
|
||||||
|
case INCORRECT:
|
||||||
|
this.status = "File incorrect";
|
||||||
|
markForUpload = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
public File getNspFile(){
|
||||||
|
return nspFile;
|
||||||
|
}
|
||||||
|
}
|
146
src/main/java/nsusbloader/Controllers/NSTableViewController.java
Normal file
146
src/main/java/nsusbloader/Controllers/NSTableViewController.java
Normal file
|
@ -0,0 +1,146 @@
|
||||||
|
package nsusbloader.Controllers;
|
||||||
|
|
||||||
|
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.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.cell.CheckBoxTableCell;
|
||||||
|
import javafx.scene.control.cell.PropertyValueFactory;
|
||||||
|
import javafx.util.Callback;
|
||||||
|
import nsusbloader.NSLDataTypes.FileStatus;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
|
public class NSTableViewController implements Initializable {
|
||||||
|
@FXML
|
||||||
|
private TableView<NSLRowModel> table;
|
||||||
|
private ObservableList<NSLRowModel> rowsObsLst;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void initialize(URL url, ResourceBundle resourceBundle) {
|
||||||
|
rowsObsLst = FXCollections.observableArrayList();
|
||||||
|
table.setPlaceholder(new Label());
|
||||||
|
|
||||||
|
TableColumn<NSLRowModel, String> statusColumn = new TableColumn<>("Status"); // TODO: Localization
|
||||||
|
TableColumn<NSLRowModel, String> fileNameColumn = new TableColumn<>("File Name"); // TODO: Localization
|
||||||
|
TableColumn<NSLRowModel, Boolean> uploadColumn = new TableColumn<>("Upload?"); // TODO: Localization
|
||||||
|
statusColumn.setMinWidth(70.0);
|
||||||
|
fileNameColumn.setMinWidth(270.0);
|
||||||
|
uploadColumn.setMinWidth(70.0);
|
||||||
|
|
||||||
|
statusColumn.setCellValueFactory(new PropertyValueFactory<>("status"));
|
||||||
|
fileNameColumn.setCellValueFactory(new PropertyValueFactory<>("nspFileName"));
|
||||||
|
// ><
|
||||||
|
uploadColumn.setCellValueFactory(new Callback<TableColumn.CellDataFeatures<NSLRowModel, Boolean>, ObservableValue<Boolean>>() {
|
||||||
|
@Override
|
||||||
|
public ObservableValue<Boolean> call(TableColumn.CellDataFeatures<NSLRowModel, Boolean> paramFeatures) {
|
||||||
|
NSLRowModel model = paramFeatures.getValue();
|
||||||
|
|
||||||
|
SimpleBooleanProperty booleanProperty = new SimpleBooleanProperty(model.isMarkForUpload());
|
||||||
|
|
||||||
|
booleanProperty.addListener(new ChangeListener<Boolean>() {
|
||||||
|
@Override
|
||||||
|
public void changed(ObservableValue<? extends Boolean> observableValue, Boolean oldValue, Boolean newValue) {
|
||||||
|
model.setMarkForUpload(newValue);
|
||||||
|
// TODO: add reference to this general class method which will validate protocol and restict selection
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return booleanProperty;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
uploadColumn.setCellFactory(new Callback<TableColumn<NSLRowModel, Boolean>, TableCell<NSLRowModel, Boolean>>() {
|
||||||
|
@Override
|
||||||
|
public TableCell<NSLRowModel, Boolean> call(TableColumn<NSLRowModel, Boolean> paramFeatures) {
|
||||||
|
CheckBoxTableCell<NSLRowModel, Boolean> cell = new CheckBoxTableCell<>();
|
||||||
|
return cell;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
table.setItems(rowsObsLst);
|
||||||
|
table.getColumns().addAll(statusColumn, fileNameColumn, uploadColumn);
|
||||||
|
|
||||||
|
rowsObsLst.add(new NSLRowModel(new File("/tmp/dump_file"), true));
|
||||||
|
rowsObsLst.add(new NSLRowModel(new File("/home/loper/тяжелые будни.mp4"), false));
|
||||||
|
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи.txt"), false));
|
||||||
|
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_2"), false));
|
||||||
|
rowsObsLst.add(new NSLRowModel(new File("/home/loper/стихи_1"), false));
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Add files when user selected them
|
||||||
|
* */
|
||||||
|
public void addFiles(List<File> files, String protocol){
|
||||||
|
rowsObsLst.clear();
|
||||||
|
if (protocol.equals("TinFoil")){
|
||||||
|
for (File nspFile: files){
|
||||||
|
rowsObsLst.add(new NSLRowModel(nspFile, true));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rowsObsLst.clear();
|
||||||
|
for (File nspFile: files){
|
||||||
|
rowsObsLst.add(new NSLRowModel(nspFile, false));
|
||||||
|
}
|
||||||
|
rowsObsLst.get(0).setMarkForUpload(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Return files ready for upload. Requested from NSLMainController only
|
||||||
|
* @return null if no files marked for upload
|
||||||
|
* List<File> if there are files
|
||||||
|
* */
|
||||||
|
public List<File> getFiles(){
|
||||||
|
List<File> files = new ArrayList<>();
|
||||||
|
if (rowsObsLst.isEmpty())
|
||||||
|
return null;
|
||||||
|
else {
|
||||||
|
for (NSLRowModel model: rowsObsLst){
|
||||||
|
if (model.isMarkForUpload())
|
||||||
|
files.add(model.getNspFile());
|
||||||
|
}
|
||||||
|
if (!files.isEmpty())
|
||||||
|
return files;
|
||||||
|
else
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Update files in case something is wrong. Requested from UsbCommunications _OR_ PFS
|
||||||
|
* */
|
||||||
|
public void reportFileStatus(String fileName, FileStatus status){
|
||||||
|
for (NSLRowModel model: rowsObsLst){
|
||||||
|
if (model.getNspFileName().equals(fileName)){
|
||||||
|
model.setStatus(status);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Called if selected different USB protocol
|
||||||
|
* */
|
||||||
|
public void protocolChangeEvent(String protocol){
|
||||||
|
if (rowsObsLst.isEmpty())
|
||||||
|
return;
|
||||||
|
if (protocol.equals("TinFoil")){
|
||||||
|
for (NSLRowModel model: rowsObsLst)
|
||||||
|
model.setMarkForUpload(true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
for (NSLRowModel model: rowsObsLst)
|
||||||
|
model.setMarkForUpload(false);
|
||||||
|
rowsObsLst.get(0).setMarkForUpload(true);
|
||||||
|
}
|
||||||
|
table.refresh();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,5 @@
|
||||||
/**
|
/**
|
||||||
Name: NSL-USBFoil
|
Name: NS-USBloader
|
||||||
@author Dmitry Isaenko
|
@author Dmitry Isaenko
|
||||||
License: GNU GPL v.3
|
License: GNU GPL v.3
|
||||||
@see https://github.com/developersu/
|
@see https://github.com/developersu/
|
||||||
|
|
|
@ -2,15 +2,14 @@ package nsusbloader;
|
||||||
|
|
||||||
import javafx.collections.FXCollections;
|
import javafx.collections.FXCollections;
|
||||||
import javafx.collections.ObservableList;
|
import javafx.collections.ObservableList;
|
||||||
|
import javafx.event.ActionEvent;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.control.Button;
|
import javafx.scene.control.*;
|
||||||
import javafx.scene.control.ChoiceBox;
|
|
||||||
import javafx.scene.control.ProgressBar;
|
|
||||||
import javafx.scene.control.TextArea;
|
|
||||||
import javafx.scene.layout.Pane;
|
import javafx.scene.layout.Pane;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
|
import nsusbloader.Controllers.NSTableViewController;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
@ -36,11 +35,13 @@ public class NSLMainController implements Initializable {
|
||||||
private ChoiceBox<String> choiceProtocol;
|
private ChoiceBox<String> choiceProtocol;
|
||||||
@FXML
|
@FXML
|
||||||
private Button switchThemeBtn;
|
private Button switchThemeBtn;
|
||||||
private Region btnSwitchImage;
|
|
||||||
|
|
||||||
@FXML
|
@FXML
|
||||||
private Pane specialPane;
|
private Pane specialPane;
|
||||||
|
|
||||||
|
@FXML
|
||||||
|
private NSTableViewController tableFilesListController;
|
||||||
|
|
||||||
private Thread usbThread;
|
private Thread usbThread;
|
||||||
|
|
||||||
private String previouslyOpenedPath;
|
private String previouslyOpenedPath;
|
||||||
|
@ -69,19 +70,22 @@ public class NSLMainController implements Initializable {
|
||||||
uploadStopBtn.getStyleClass().add("buttonUp");
|
uploadStopBtn.getStyleClass().add("buttonUp");
|
||||||
uploadStopBtn.setGraphic(btnUpStopImage);
|
uploadStopBtn.setGraphic(btnUpStopImage);
|
||||||
|
|
||||||
ObservableList<String> choiceProtocolList = FXCollections.observableArrayList();
|
ObservableList<String> choiceProtocolList = FXCollections.observableArrayList("TinFoil", "GoldLeaf");
|
||||||
choiceProtocolList.setAll("TinFoil", "GoldLeaf");
|
|
||||||
choiceProtocol.setItems(choiceProtocolList);
|
choiceProtocol.setItems(choiceProtocolList);
|
||||||
choiceProtocol.getSelectionModel().select(0); // TODO: shared settings
|
choiceProtocol.getSelectionModel().select(0); // TODO: shared settings
|
||||||
|
choiceProtocol.setOnAction(e->tableFilesListController.protocolChangeEvent(choiceProtocol.getSelectionModel().getSelectedItem()));
|
||||||
|
|
||||||
this.previouslyOpenedPath = null;
|
this.previouslyOpenedPath = null;
|
||||||
|
|
||||||
this.btnSwitchImage = new Region();
|
Region btnSwitchImage = new Region();
|
||||||
btnSwitchImage.getStyleClass().add("regionLamp");
|
btnSwitchImage.getStyleClass().add("regionLamp");
|
||||||
switchThemeBtn.setGraphic(btnSwitchImage);
|
switchThemeBtn.setGraphic(btnSwitchImage);
|
||||||
this.switchThemeBtn.setOnAction(e->switchTheme());
|
this.switchThemeBtn.setOnAction(e->switchTheme());
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Changes UI theme on the go
|
||||||
|
* */
|
||||||
private void switchTheme(){
|
private void switchTheme(){
|
||||||
if (switchThemeBtn.getScene().getStylesheets().get(0).equals("/res/app.css")) {
|
if (switchThemeBtn.getScene().getStylesheets().get(0).equals("/res/app.css")) {
|
||||||
switchThemeBtn.getScene().getStylesheets().remove("/res/app.css");
|
switchThemeBtn.getScene().getStylesheets().remove("/res/app.css");
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
package nsusbloader.PFS;
|
package nsusbloader.PFS;
|
||||||
|
|
||||||
|
import nsusbloader.NSLDataTypes.MsgType;
|
||||||
import nsusbloader.ServiceWindow;
|
import nsusbloader.ServiceWindow;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
@ -18,7 +19,6 @@ 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 static final byte[] PFS0 = new byte[]{(byte)0x50, (byte)0x46, (byte)0x53, (byte)0x30}; // PFS0, and what did you think?
|
||||||
|
|
||||||
private BlockingQueue<String> msgQueue;
|
private BlockingQueue<String> msgQueue;
|
||||||
private enum MsgType {PASS, FAIL, INFO, WARNING}
|
|
||||||
private ResourceBundle rb;
|
private ResourceBundle rb;
|
||||||
|
|
||||||
private RandomAccessFile randAccessFile;
|
private RandomAccessFile randAccessFile;
|
||||||
|
@ -106,7 +106,6 @@ public class PFSProvider {
|
||||||
for (int i=0; i<filesCount; i++){
|
for (int i=0; i<filesCount; i++){
|
||||||
if (randAccessFile.read(ncaInfoArr) == 24) {
|
if (randAccessFile.read(ncaInfoArr) == 24) {
|
||||||
printLog("Read NCA inside NSP: " + i, MsgType.PASS);
|
printLog("Read NCA inside NSP: " + i, MsgType.PASS);
|
||||||
//hexDumpUTF8(ncaInfoArr); // TODO: DEBUG
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
printLog("Read NCA inside NSP: "+i, MsgType.FAIL);
|
printLog("Read NCA inside NSP: "+i, MsgType.FAIL);
|
||||||
|
@ -149,7 +148,6 @@ public class PFSProvider {
|
||||||
printLog("Final padding check", MsgType.PASS);
|
printLog("Final padding check", MsgType.PASS);
|
||||||
else
|
else
|
||||||
printLog("Final padding check", MsgType.WARNING);
|
printLog("Final padding check", MsgType.WARNING);
|
||||||
//hexDumpUTF8(bufForInt); // TODO: DEBUG
|
|
||||||
|
|
||||||
// Calculate position including header for body size offset
|
// Calculate position including header for body size offset
|
||||||
bodySize = randAccessFile.getFilePointer()+header;
|
bodySize = randAccessFile.getFilePointer()+header;
|
||||||
|
@ -174,7 +172,6 @@ public class PFSProvider {
|
||||||
if (new String(exchangeTempArray, StandardCharsets.UTF_8).toLowerCase().endsWith(".tik"))
|
if (new String(exchangeTempArray, StandardCharsets.UTF_8).toLowerCase().endsWith(".tik"))
|
||||||
this.ticketID = i;
|
this.ticketID = i;
|
||||||
this.ncaFiles[i].setNcaFileName(Arrays.copyOf(exchangeTempArray, exchangeTempArray.length));
|
this.ncaFiles[i].setNcaFileName(Arrays.copyOf(exchangeTempArray, exchangeTempArray.length));
|
||||||
//hexDumpUTF8(exchangeTempArray); // TODO: DEBUG
|
|
||||||
}
|
}
|
||||||
randAccessFile.close();
|
randAccessFile.close();
|
||||||
}
|
}
|
||||||
|
@ -256,7 +253,7 @@ public class PFSProvider {
|
||||||
msgQueue.put(message);
|
msgQueue.put(message);
|
||||||
}
|
}
|
||||||
}catch (InterruptedException ie){
|
}catch (InterruptedException ie){
|
||||||
ie.printStackTrace();
|
ie.printStackTrace(); //TODO: INFORM
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package nsusbloader;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.scene.control.ProgressBar;
|
import javafx.scene.control.ProgressBar;
|
||||||
import javafx.scene.control.TextArea;
|
import javafx.scene.control.TextArea;
|
||||||
|
import nsusbloader.NSLDataTypes.MsgType;
|
||||||
import nsusbloader.PFS.PFSProvider;
|
import nsusbloader.PFS.PFSProvider;
|
||||||
import org.usb4java.*;
|
import org.usb4java.*;
|
||||||
|
|
||||||
|
@ -18,7 +19,6 @@ import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.BlockingQueue;
|
import java.util.concurrent.BlockingQueue;
|
||||||
import java.util.concurrent.LinkedBlockingQueue;
|
import java.util.concurrent.LinkedBlockingQueue;
|
||||||
import java.util.concurrent.TimeUnit;
|
|
||||||
|
|
||||||
import static nsusbloader.RainbowHexDump.hexDumpUTF8;
|
import static nsusbloader.RainbowHexDump.hexDumpUTF8;
|
||||||
|
|
||||||
|
@ -26,8 +26,7 @@ class UsbCommunications extends Task<Void> {
|
||||||
private final int DEFAULT_INTERFACE = 0;
|
private final int DEFAULT_INTERFACE = 0;
|
||||||
|
|
||||||
private BlockingQueue<String> msgQueue;
|
private BlockingQueue<String> msgQueue;
|
||||||
private BlockingQueue<Double> progressQueue ;
|
private BlockingQueue<Double> progressQueue;
|
||||||
private enum MsgType {PASS, FAIL, INFO, WARNING}
|
|
||||||
private MessagesConsumer msgConsumer;
|
private MessagesConsumer msgConsumer;
|
||||||
|
|
||||||
private HashMap<String, File> nspMap;
|
private HashMap<String, File> nspMap;
|
||||||
|
@ -268,9 +267,7 @@ class UsbCommunications extends Task<Void> {
|
||||||
|
|
||||||
//--------------------------------------------------------------------------------------------------------------
|
//--------------------------------------------------------------------------------------------------------------
|
||||||
if (protocol.equals("TinFoil")) {
|
if (protocol.equals("TinFoil")) {
|
||||||
if (!sendListOfNSP())
|
new TinFoil();
|
||||||
return null;
|
|
||||||
proceedCommands();
|
|
||||||
} else {
|
} else {
|
||||||
new GoldLeaf();
|
new GoldLeaf();
|
||||||
}
|
}
|
||||||
|
@ -280,339 +277,247 @@ class UsbCommunications extends Task<Void> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Correct exit
|
* Tinfoil processing
|
||||||
* */
|
* */
|
||||||
private void close(){
|
private class TinFoil{
|
||||||
// close handler in the end
|
TinFoil(){
|
||||||
if (handlerNS != null) {
|
if (!sendListOfNSP())
|
||||||
// Try to release interface
|
return;
|
||||||
int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
|
proceedCommands();
|
||||||
|
}
|
||||||
if (result != LibUsb.SUCCESS)
|
/**
|
||||||
printLog("Release interface\n Returned: "+result+" (sometimes it's not an issue)", MsgType.WARNING);
|
* Send what NSP will be transferred
|
||||||
|
* */
|
||||||
|
private boolean sendListOfNSP(){
|
||||||
|
// Send list of NSP files:
|
||||||
|
// Proceed "TUL0"
|
||||||
|
if (!writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30}
|
||||||
|
printLog("Send list of files: handshake", MsgType.FAIL);
|
||||||
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
printLog("Release interface", MsgType.PASS);
|
printLog("Send list of files: handshake", MsgType.PASS);
|
||||||
|
//Collect file names
|
||||||
|
StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
|
||||||
|
for(String nspFileName: nspMap.keySet())
|
||||||
|
nspListNamesBuilder.append(nspFileName+'\n'); // And here we come with java string default encoding (UTF-16)
|
||||||
|
|
||||||
LibUsb.close(handlerNS);
|
byte[] nspListNames = nspListNamesBuilder.toString().getBytes(StandardCharsets.UTF_8);
|
||||||
printLog("Requested handler close", MsgType.INFO);
|
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
|
||||||
}
|
byteBuffer.putInt(nspListNames.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
|
||||||
// close context in the end
|
byte[] nspListSize = byteBuffer.array(); // TODO: rewind? not sure..
|
||||||
if (contextNS != null) {
|
//byteBuffer.reset();
|
||||||
LibUsb.exit(contextNS);
|
|
||||||
printLog("Requested context close", MsgType.INFO);
|
|
||||||
}
|
|
||||||
msgConsumer.interrupt();
|
|
||||||
}
|
|
||||||
private boolean sendListOfNSP(){
|
|
||||||
// Send list of NSP files:
|
|
||||||
// Proceed "TUL0"
|
|
||||||
if (!writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30}
|
|
||||||
printLog("Send list of files: handshake", MsgType.FAIL);
|
|
||||||
close();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
printLog("Send list of files: handshake", MsgType.PASS);
|
|
||||||
//Collect file names
|
|
||||||
StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
|
|
||||||
for(String nspFileName: nspMap.keySet())
|
|
||||||
nspListNamesBuilder.append(nspFileName+'\n'); // And here we come with java string default encoding (UTF-16)
|
|
||||||
|
|
||||||
byte[] nspListNames = nspListNamesBuilder.toString().getBytes(StandardCharsets.UTF_8);
|
// Sending NSP list
|
||||||
ByteBuffer byteBuffer = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN); // integer = 4 bytes; BTW Java is stored in big-endian format
|
if (!writeToUsb(nspListSize)) { // size of the list we're going to transfer goes...
|
||||||
byteBuffer.putInt(nspListNames.length); // This way we obtain length in int converted to byte array in correct Big-endian order. Trust me.
|
printLog("Send list of files: send length.", MsgType.FAIL);
|
||||||
byte[] nspListSize = byteBuffer.array(); // TODO: rewind? not sure..
|
close();
|
||||||
//byteBuffer.reset();
|
return false;
|
||||||
|
} else
|
||||||
// Sending NSP list
|
printLog("Send list of files: send length.", MsgType.PASS);
|
||||||
if (!writeToUsb(nspListSize)) { // size of the list we're going to transfer goes...
|
if (!writeToUsb(new byte[8])) { // 8 zero bytes goes...
|
||||||
printLog("Send list of files: send length.", MsgType.FAIL);
|
printLog("Send list of files: send padding.", MsgType.FAIL);
|
||||||
close();
|
close();
|
||||||
return false;
|
return false;
|
||||||
} else
|
}
|
||||||
printLog("Send list of files: send length.", MsgType.PASS);
|
else
|
||||||
if (!writeToUsb(new byte[8])) { // 8 zero bytes goes...
|
printLog("Send list of files: send padding.", MsgType.PASS);
|
||||||
printLog("Send list of files: send padding.", MsgType.FAIL);
|
if (!writeToUsb(nspListNames)) { // list of the names goes...
|
||||||
close();
|
printLog("Send list of files: send list itself.", MsgType.FAIL);
|
||||||
return false;
|
close();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printLog("Send list of files: send list itself.", MsgType.PASS);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
else
|
/**
|
||||||
printLog("Send list of files: send padding.", MsgType.PASS);
|
* After we sent commands to NS, this chain starts
|
||||||
if (!writeToUsb(nspListNames)) { // list of the names goes...
|
* */
|
||||||
printLog("Send list of files: send list itself.", MsgType.FAIL);
|
private void proceedCommands(){
|
||||||
close();
|
printLog("Awaiting for NS commands.", MsgType.INFO);
|
||||||
return false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
printLog("Send list of files: send list itself.", MsgType.PASS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
/**
|
|
||||||
* After we sent commands to NS, this chain starts
|
|
||||||
* */
|
|
||||||
private void proceedCommands(){
|
|
||||||
printLog("Awaiting for NS commands.", MsgType.INFO);
|
|
||||||
|
|
||||||
/* byte[] magic = new byte[4];
|
/* byte[] magic = new byte[4];
|
||||||
ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic);
|
ByteBuffer bb = StandardCharsets.UTF_8.encode("TUC0").rewind().get(magic);
|
||||||
// Let's rephrase this 'string' */
|
// Let's rephrase this 'string' */
|
||||||
final byte[] magic = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // eq. 'TUC0' @ UTF-8 (actually ASCII lol, u know what I mean)
|
final byte[] magic = new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30}; // eq. 'TUC0' @ UTF-8 (actually ASCII lol, u know what I mean)
|
||||||
|
|
||||||
byte[] receivedArray;
|
byte[] receivedArray;
|
||||||
|
|
||||||
while (true){
|
while (true){
|
||||||
if (Thread.currentThread().isInterrupted()) // Check if user interrupted process.
|
if (Thread.currentThread().isInterrupted()) // Check if user interrupted process.
|
||||||
return;
|
return;
|
||||||
receivedArray = readFromUsb();
|
receivedArray = readFromUsb();
|
||||||
if (receivedArray == null)
|
if (receivedArray == null)
|
||||||
return; // catches exception
|
return; // catches exception
|
||||||
|
|
||||||
if (!Arrays.equals(Arrays.copyOfRange(receivedArray, 0,4), magic)) // Bytes from 0 to 3 should contain 'magic' TUC0, so must be verified like this
|
if (!Arrays.equals(Arrays.copyOfRange(receivedArray, 0,4), magic)) // Bytes from 0 to 3 should contain 'magic' TUC0, so must be verified like this
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
// 8th to 12th(explicits) bytes in returned data stands for command ID as unsigned integer (Little-endian). Actually, we have to compare arrays here, but in real world it can't be greater then 0/1/2, thus:
|
// 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.
|
// 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
|
if (receivedArray[8] == 0x00){ //0x00 - exit
|
||||||
printLog("Received EXIT command. Terminating.", MsgType.PASS);
|
printLog("Received EXIT command. Terminating.", MsgType.PASS);
|
||||||
return; // All interaction with USB device should be ended (expected);
|
return; // 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).
|
else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack).
|
||||||
printLog("Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", MsgType.PASS);
|
printLog("Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", MsgType.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.
|
/*// 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();
|
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
|
logsArea.appendText("[V] Received FILE_RANGE command. Size: "+Long.toUnsignedString(receivedSize)+"\n"); // this shit returns string that will be chosen next '+32'. And, BTW, can't be greater then 512
|
||||||
*/
|
*/
|
||||||
if (!fileRangeCmd()) {
|
if (!fileRangeCmd()) {
|
||||||
return; // catches exception
|
return; // catches exception
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
/**
|
||||||
/**
|
* This is what returns requested file (files)
|
||||||
* This is what returns requested file (files)
|
* Executes multiple times
|
||||||
* Executes multiple times
|
* @return 'true' if everything is ok
|
||||||
* @return 'true' if everything is ok
|
* 'false' is error/exception occurs
|
||||||
* 'false' is error/exception occurs
|
* */
|
||||||
* */
|
private boolean fileRangeCmd(){
|
||||||
private boolean fileRangeCmd(){
|
boolean isProgessBarInitiated = false;
|
||||||
boolean isProgessBarInitiated = false;
|
|
||||||
|
|
||||||
byte[] receivedArray;
|
byte[] receivedArray;
|
||||||
// Here we take information of what other side wants
|
// Here we take information of what other side wants
|
||||||
receivedArray = readFromUsb();
|
receivedArray = readFromUsb();
|
||||||
if (receivedArray == null)
|
if (receivedArray == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// range_offset of the requested file. In the begining it will be 0x10.
|
// range_offset of the requested file. In the begining it will be 0x10.
|
||||||
long receivedRangeSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 0,8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
|
long receivedRangeSize = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 0,8)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
|
||||||
byte[] receivedRangeSizeRAW = Arrays.copyOfRange(receivedArray, 0,8); // used (only) when we use sendResponse(). It's just simply.
|
byte[] receivedRangeSizeRAW = Arrays.copyOfRange(receivedArray, 0,8); // used (only) when we use sendResponse(). It's just simply.
|
||||||
long receivedRangeOffset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
|
long receivedRangeOffset = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 8,16)).order(ByteOrder.LITTLE_ENDIAN).getLong(); // Note - it could be unsigned long. Unfortunately, this app won't support files greater then 8796093022208 Gb
|
||||||
/* Below, it's REAL NSP file name length that we sent before among others (WITHOUT +32 byes). It can't be greater then... see what is written in the beginning of this code.
|
/* Below, it's REAL NSP file name length that we sent before among others (WITHOUT +32 byes). It can't be greater then... see what is written in the beginning of this code.
|
||||||
We don't need this since in next pocket we'll get name itself UTF-8 encoded. Could be used to double-checks or something like that.
|
We don't need this since in next pocket we'll get name itself UTF-8 encoded. Could be used to double-checks or something like that.
|
||||||
long receivedNspNameLen = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 16,24)).order(ByteOrder.LITTLE_ENDIAN).getLong(); */
|
long receivedNspNameLen = ByteBuffer.wrap(Arrays.copyOfRange(receivedArray, 16,24)).order(ByteOrder.LITTLE_ENDIAN).getLong(); */
|
||||||
|
|
||||||
// Requesting UTF-8 file name required:
|
// Requesting UTF-8 file name required:
|
||||||
receivedArray = readFromUsb();
|
receivedArray = readFromUsb();
|
||||||
if (receivedArray == null)
|
if (receivedArray == null)
|
||||||
return false;
|
|
||||||
|
|
||||||
String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8);
|
|
||||||
printLog("Reply to requested file: "+receivedRequestedNSP
|
|
||||||
+"\n Range Size: "+receivedRangeSize
|
|
||||||
+"\n Range Offset: "+receivedRangeOffset, MsgType.INFO);
|
|
||||||
|
|
||||||
// Sending response header
|
|
||||||
if (!sendResponse(receivedRangeSizeRAW)) // Get receivedRangeSize in 'RAW' format exactly as it has been received. It's simply.
|
|
||||||
return false;
|
|
||||||
|
|
||||||
// Read file starting:
|
|
||||||
// from Range Offset (receivedRangeOffset)
|
|
||||||
// to Range Size (receivedRangeSize) like end: receivedRangeOffset+receivedRangeSize
|
|
||||||
|
|
||||||
try {
|
|
||||||
|
|
||||||
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
|
|
||||||
byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
|
|
||||||
int bufferLength;
|
|
||||||
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
|
|
||||||
printLog("Requested skip is out of File size. Nothing to transmit.", MsgType.FAIL);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
long currentOffset = 0;
|
String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8);
|
||||||
// 'End Offset' equal to receivedRangeSize.
|
printLog("Reply to requested file: "+receivedRequestedNSP
|
||||||
int readPice = 8388608; // = 8Mb
|
+"\n Range Size: "+receivedRangeSize
|
||||||
|
+"\n Range Offset: "+receivedRangeOffset, MsgType.INFO);
|
||||||
|
|
||||||
while (currentOffset < receivedRangeSize){
|
// Sending response header
|
||||||
if (Thread.currentThread().isInterrupted()) // Check if user interrupted process.
|
if (!sendResponse(receivedRangeSizeRAW)) // Get receivedRangeSize in 'RAW' format exactly as it has been received. It's simply.
|
||||||
return true;
|
return false;
|
||||||
if ((currentOffset + readPice) >= receivedRangeSize )
|
|
||||||
readPice = Math.toIntExact(receivedRangeSize - currentOffset);
|
|
||||||
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // TODO: NOTE: -----------------------DEBUG-----------------
|
|
||||||
// updating progress bar (if a lot of data requested) START BLOCK
|
|
||||||
if (isProgessBarInitiated){
|
|
||||||
try {
|
|
||||||
if (currentOffset+readPice == receivedRangeOffset){
|
|
||||||
progressQueue.put(1.0);
|
|
||||||
isProgessBarInitiated = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
progressQueue.put((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
|
|
||||||
}catch (InterruptedException ie){
|
|
||||||
getException().printStackTrace(); // TODO: Do something with this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if ((readPice == 8388608) && (currentOffset == 0))
|
|
||||||
isProgessBarInitiated = true;
|
|
||||||
}
|
|
||||||
// updating progress bar if needed END BLOCK
|
|
||||||
|
|
||||||
bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
// Read file starting:
|
||||||
|
// from Range Offset (receivedRangeOffset)
|
||||||
|
// to Range Size (receivedRangeSize) like end: receivedRangeOffset+receivedRangeSize
|
||||||
|
|
||||||
bufferLength = bufferedInStream.read(bufferCurrent);
|
try {
|
||||||
|
|
||||||
if (bufferLength != -1){
|
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
|
||||||
//write to USB
|
byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
|
||||||
if (!writeToUsb(bufferCurrent)) {
|
int bufferLength;
|
||||||
printLog("Failure during NSP transmission.", MsgType.FAIL);
|
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
|
||||||
return false;
|
printLog("Requested skip is out of File size. Nothing to transmit.", MsgType.FAIL);
|
||||||
}
|
|
||||||
currentOffset += readPice;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
printLog("Unexpected reading of stream ended.", MsgType.WARNING);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
long currentOffset = 0;
|
||||||
bufferedInStream.close();
|
// 'End Offset' equal to receivedRangeSize.
|
||||||
} catch (FileNotFoundException fnfe){
|
int readPice = 8388608; // = 8Mb
|
||||||
printLog("FileNotFoundException:\n"+fnfe.getMessage(), MsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
} catch (IOException ioe){
|
|
||||||
printLog("IOException:\n"+ioe.getMessage(), MsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
} catch (ArithmeticException ae){
|
|
||||||
printLog("ArithmeticException (can't cast end offset minus current to 'integer'):\n"+ae.getMessage(), MsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
while (currentOffset < receivedRangeSize){
|
||||||
}
|
if (Thread.currentThread().isInterrupted()) // Check if user interrupted process.
|
||||||
/**
|
return true;
|
||||||
* Send response header.
|
if ((currentOffset + readPice) >= receivedRangeSize )
|
||||||
* @return true if everything OK
|
readPice = Math.toIntExact(receivedRangeSize - currentOffset);
|
||||||
* false if failed
|
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // TODO: NOTE: -----------------------DEBUG-----------------
|
||||||
* */
|
// updating progress bar (if a lot of data requested) START BLOCK
|
||||||
private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
|
if (isProgessBarInitiated){
|
||||||
printLog("Sending response", MsgType.INFO);
|
try {
|
||||||
if (!writeToUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0'
|
if (currentOffset+readPice == receivedRangeOffset){
|
||||||
(byte) 0x01, // CMD_TYPE_RESPONSE = 1
|
progressQueue.put(1.0);
|
||||||
(byte) 0x00, (byte) 0x00, (byte) 0x00, // kinda padding. Guys, didn't you want to use integer value for CMD semantic?
|
isProgessBarInitiated = false;
|
||||||
(byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00} ) // Send integer value of '1' in Little-endian format.
|
}
|
||||||
){
|
else
|
||||||
printLog("[1/3]", MsgType.FAIL);
|
progressQueue.put((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
|
||||||
return false;
|
}catch (InterruptedException ie){
|
||||||
}
|
getException().printStackTrace(); // TODO: Do something with this
|
||||||
printLog("[1/3]", MsgType.PASS);
|
}
|
||||||
if(!writeToUsb(rangeSize)) { // Send EXACTLY what has been received
|
}
|
||||||
printLog("[2/3]", MsgType.FAIL);
|
else {
|
||||||
return false;
|
if ((readPice == 8388608) && (currentOffset == 0))
|
||||||
}
|
isProgessBarInitiated = true;
|
||||||
printLog("[2/3]", MsgType.PASS);
|
}
|
||||||
if(!writeToUsb(new byte[12])) { // kinda another one padding
|
// updating progress bar if needed END BLOCK
|
||||||
printLog("[3/3]", MsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
printLog("[3/3]", MsgType.PASS);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
||||||
* Sending any byte array to USB device
|
|
||||||
* @return 'true' if no issues
|
bufferLength = bufferedInStream.read(bufferCurrent);
|
||||||
* 'false' if errors happened
|
|
||||||
* */
|
if (bufferLength != -1){
|
||||||
private boolean writeToUsb(byte[] message){
|
//write to USB
|
||||||
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
|
if (!writeToUsb(bufferCurrent)) {
|
||||||
writeBuffer.put(message);
|
printLog("Failure during NSP transmission.", MsgType.FAIL);
|
||||||
// DONT EVEN THINK OF USING writeBuffer.rewind(); // well..
|
return false;
|
||||||
IntBuffer writeBufTransferred = IntBuffer.allocate(1);
|
}
|
||||||
int result;
|
currentOffset += readPice;
|
||||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
|
}
|
||||||
if (result != LibUsb.SUCCESS){
|
else {
|
||||||
switch (result){
|
printLog("Unexpected reading of stream ended.", MsgType.WARNING);
|
||||||
case LibUsb.ERROR_TIMEOUT:
|
return false;
|
||||||
printLog("Data transfer (write) issue\n Returned: ERROR_TIMEOUT", MsgType.FAIL);
|
}
|
||||||
break;
|
|
||||||
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
|
}
|
||||||
printLog("Data transfer (write) issue\n Returned: ERROR_PIPE", MsgType.FAIL);
|
bufferedInStream.close();
|
||||||
break;
|
} catch (FileNotFoundException fnfe){
|
||||||
case LibUsb.ERROR_OVERFLOW:
|
printLog("FileNotFoundException:\n"+fnfe.getMessage(), MsgType.FAIL);
|
||||||
printLog("Data transfer (write) issue\n Returned: ERROR_OVERFLOW", MsgType.FAIL);
|
return false;
|
||||||
break;
|
} catch (IOException ioe){
|
||||||
case LibUsb.ERROR_NO_DEVICE:
|
printLog("IOException:\n"+ioe.getMessage(), MsgType.FAIL);
|
||||||
printLog("Data transfer (write) issue\n Returned: ERROR_NO_DEVICE", MsgType.FAIL);
|
return false;
|
||||||
break;
|
} catch (ArithmeticException ae){
|
||||||
default:
|
printLog("ArithmeticException (can't cast end offset minus current to 'integer'):\n"+ae.getMessage(), MsgType.FAIL);
|
||||||
printLog("Data transfer (write) issue\n Returned: "+result, MsgType.FAIL);
|
|
||||||
}
|
|
||||||
printLog("Execution stopped", MsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
}else {
|
|
||||||
if (writeBufTransferred.get() != message.length){
|
|
||||||
printLog("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), MsgType.FAIL);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Send response header.
|
||||||
|
* @return true if everything OK
|
||||||
|
* false if failed
|
||||||
|
* */
|
||||||
|
private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
|
||||||
|
printLog("Sending response", MsgType.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]", MsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printLog("[1/3]", MsgType.PASS);
|
||||||
|
if(!writeToUsb(rangeSize)) { // Send EXACTLY what has been received
|
||||||
|
printLog("[2/3]", MsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printLog("[2/3]", MsgType.PASS);
|
||||||
|
if(!writeToUsb(new byte[12])) { // kinda another one padding
|
||||||
|
printLog("[3/3]", MsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
printLog("[3/3]", MsgType.PASS);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Reading what USB device responded.
|
* Tinfoil processing
|
||||||
* @return byte array if data read successful
|
|
||||||
* 'null' if read failed
|
|
||||||
* */
|
* */
|
||||||
private byte[] readFromUsb(){
|
|
||||||
ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);// //readBuffer.order() equals BIG_ENDIAN; DON'T TOUCH. And we will always allocate readBuffer for max-size endpoint supports (512 bytes)
|
|
||||||
// We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
|
|
||||||
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
|
||||||
|
|
||||||
int result;
|
|
||||||
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
|
|
||||||
|
|
||||||
if (result != LibUsb.SUCCESS){
|
|
||||||
switch (result){
|
|
||||||
case LibUsb.ERROR_TIMEOUT:
|
|
||||||
printLog("Data transfer (read) issue\n Returned: ERROR_TIMEOUT", MsgType.FAIL);
|
|
||||||
break;
|
|
||||||
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
|
|
||||||
printLog("Data transfer (read) issue\n Returned: ERROR_PIPE", MsgType.FAIL);
|
|
||||||
break;
|
|
||||||
case LibUsb.ERROR_OVERFLOW:
|
|
||||||
printLog("Data transfer (read) issue\n Returned: ERROR_OVERFLOW", MsgType.FAIL);
|
|
||||||
break;
|
|
||||||
case LibUsb.ERROR_NO_DEVICE:
|
|
||||||
printLog("Data transfer (read) issue\n Returned: ERROR_NO_DEVICE", MsgType.FAIL);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
printLog("Data transfer (read) issue\n Returned: "+result, MsgType.FAIL);
|
|
||||||
}
|
|
||||||
printLog("Execution stopped", MsgType.FAIL);
|
|
||||||
return null;
|
|
||||||
} else {
|
|
||||||
int trans = readBufTransferred.get();
|
|
||||||
byte[] receivedBytes = new byte[trans];
|
|
||||||
readBuffer.get(receivedBytes);
|
|
||||||
/* DEBUG START----------------------------------------------------------------------------------------------*
|
|
||||||
hexDumpUTF8(receivedBytes);
|
|
||||||
// DEBUG END----------------------------------------------------------------------------------------------*/
|
|
||||||
return receivedBytes;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class GoldLeaf{
|
private class GoldLeaf{
|
||||||
// CMD G L U C ID 0 0 0
|
// CMD G L U C ID 0 0 0
|
||||||
private final byte[] CMD_ConnectionRequest = new byte[]{0x47, 0x4c, 0x55, 0x43, 0x00, 0x00, 0x00, 0x00}; // Write-only command
|
private final byte[] CMD_ConnectionRequest = new byte[]{0x47, 0x4c, 0x55, 0x43, 0x00, 0x00, 0x00, 0x00}; // Write-only command
|
||||||
|
@ -807,8 +712,118 @@ class UsbCommunications extends Task<Void> {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//------------------------------------------------------------------------------------------------------------------
|
//------------------------------------------------------------------------------------------------------------------
|
||||||
|
/**
|
||||||
|
* Correct exit
|
||||||
|
* */
|
||||||
|
private void close(){
|
||||||
|
// close handler in the end
|
||||||
|
if (handlerNS != null) {
|
||||||
|
// Try to release interface
|
||||||
|
int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
|
||||||
|
|
||||||
|
if (result != LibUsb.SUCCESS)
|
||||||
|
printLog("Release interface\n Returned: "+result+" (sometimes it's not an issue)", MsgType.WARNING);
|
||||||
|
else
|
||||||
|
printLog("Release interface", MsgType.PASS);
|
||||||
|
|
||||||
|
LibUsb.close(handlerNS);
|
||||||
|
printLog("Requested handler close", MsgType.INFO);
|
||||||
|
}
|
||||||
|
// close context in the end
|
||||||
|
if (contextNS != null) {
|
||||||
|
LibUsb.exit(contextNS);
|
||||||
|
printLog("Requested context close", MsgType.INFO);
|
||||||
|
}
|
||||||
|
msgConsumer.interrupt();
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Sending any byte array to USB device
|
||||||
|
* @return 'true' if no issues
|
||||||
|
* 'false' if errors happened
|
||||||
|
* */
|
||||||
|
private boolean writeToUsb(byte[] message){
|
||||||
|
ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN;
|
||||||
|
writeBuffer.put(message);
|
||||||
|
// DONT EVEN THINK OF USING writeBuffer.rewind(); // well..
|
||||||
|
IntBuffer writeBufTransferred = IntBuffer.allocate(1);
|
||||||
|
int result;
|
||||||
|
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
|
||||||
|
if (result != LibUsb.SUCCESS){
|
||||||
|
switch (result){
|
||||||
|
case LibUsb.ERROR_TIMEOUT:
|
||||||
|
printLog("Data transfer (write) issue\n Returned: ERROR_TIMEOUT", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
|
||||||
|
printLog("Data transfer (write) issue\n Returned: ERROR_PIPE", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_OVERFLOW:
|
||||||
|
printLog("Data transfer (write) issue\n Returned: ERROR_OVERFLOW", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_NO_DEVICE:
|
||||||
|
printLog("Data transfer (write) issue\n Returned: ERROR_NO_DEVICE", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printLog("Data transfer (write) issue\n Returned: "+result, MsgType.FAIL);
|
||||||
|
}
|
||||||
|
printLog("Execution stopped", MsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}else {
|
||||||
|
if (writeBufTransferred.get() != message.length){
|
||||||
|
printLog("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), MsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Reading what USB device responded.
|
||||||
|
* @return byte array if data read successful
|
||||||
|
* 'null' if read failed
|
||||||
|
* */
|
||||||
|
private byte[] readFromUsb(){
|
||||||
|
ByteBuffer readBuffer = ByteBuffer.allocateDirect(512);// //readBuffer.order() equals BIG_ENDIAN; DON'T TOUCH. And we will always allocate readBuffer for max-size endpoint supports (512 bytes)
|
||||||
|
// We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb.
|
||||||
|
IntBuffer readBufTransferred = IntBuffer.allocate(1);
|
||||||
|
|
||||||
|
int result;
|
||||||
|
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
|
||||||
|
|
||||||
|
if (result != LibUsb.SUCCESS){
|
||||||
|
switch (result){
|
||||||
|
case LibUsb.ERROR_TIMEOUT:
|
||||||
|
printLog("Data transfer (read) issue\n Returned: ERROR_TIMEOUT", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
|
||||||
|
printLog("Data transfer (read) issue\n Returned: ERROR_PIPE", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_OVERFLOW:
|
||||||
|
printLog("Data transfer (read) issue\n Returned: ERROR_OVERFLOW", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_NO_DEVICE:
|
||||||
|
printLog("Data transfer (read) issue\n Returned: ERROR_NO_DEVICE", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
case LibUsb.ERROR_IO:
|
||||||
|
printLog("Data transfer (read) issue\n Returned: ERROR_IO", MsgType.FAIL);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
printLog("Data transfer (read) issue\n Returned: "+result, MsgType.FAIL);
|
||||||
|
}
|
||||||
|
printLog("Execution stopped", MsgType.FAIL);
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
int trans = readBufTransferred.get();
|
||||||
|
byte[] receivedBytes = new byte[trans];
|
||||||
|
readBuffer.get(receivedBytes);
|
||||||
|
/* DEBUG START----------------------------------------------------------------------------------------------*
|
||||||
|
hexDumpUTF8(receivedBytes);
|
||||||
|
// DEBUG END----------------------------------------------------------------------------------------------*/
|
||||||
|
return receivedBytes;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is what will print to textArea of the application.
|
* This is what will print to textArea of the application.
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -4,6 +4,8 @@
|
||||||
<?import javafx.scene.control.Button?>
|
<?import javafx.scene.control.Button?>
|
||||||
<?import javafx.scene.control.ChoiceBox?>
|
<?import javafx.scene.control.ChoiceBox?>
|
||||||
<?import javafx.scene.control.ProgressBar?>
|
<?import javafx.scene.control.ProgressBar?>
|
||||||
|
<?import javafx.scene.control.Tab?>
|
||||||
|
<?import javafx.scene.control.TabPane?>
|
||||||
<?import javafx.scene.control.TextArea?>
|
<?import javafx.scene.control.TextArea?>
|
||||||
<?import javafx.scene.control.ToolBar?>
|
<?import javafx.scene.control.ToolBar?>
|
||||||
<?import javafx.scene.layout.AnchorPane?>
|
<?import javafx.scene.layout.AnchorPane?>
|
||||||
|
@ -19,31 +21,61 @@
|
||||||
<children>
|
<children>
|
||||||
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
<VBox AnchorPane.bottomAnchor="0.0" AnchorPane.leftAnchor="0.0" AnchorPane.rightAnchor="0.0" AnchorPane.topAnchor="0.0">
|
||||||
<children>
|
<children>
|
||||||
<ToolBar>
|
<TabPane prefHeight="200.0" prefWidth="200.0" side="RIGHT" tabClosingPolicy="UNAVAILABLE" VBox.vgrow="ALWAYS">
|
||||||
<items>
|
<tabs>
|
||||||
<Pane HBox.hgrow="ALWAYS" />
|
<Tab closable="false">
|
||||||
<ChoiceBox fx:id="choiceProtocol" prefWidth="120.0" />
|
<content>
|
||||||
<Button fx:id="switchThemeBtn" mnemonicParsing="false" />
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
</items>
|
<children>
|
||||||
</ToolBar>
|
<ToolBar>
|
||||||
<GridPane>
|
<items>
|
||||||
<columnConstraints>
|
<ChoiceBox fx:id="choiceProtocol" prefWidth="120.0" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" />
|
<Pane HBox.hgrow="ALWAYS" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" percentWidth="90.0" />
|
<Button fx:id="switchThemeBtn" mnemonicParsing="false" />
|
||||||
<ColumnConstraints hgrow="SOMETIMES" />
|
</items>
|
||||||
</columnConstraints>
|
</ToolBar>
|
||||||
<rowConstraints>
|
<GridPane>
|
||||||
<RowConstraints vgrow="SOMETIMES" />
|
<columnConstraints>
|
||||||
</rowConstraints>
|
<ColumnConstraints hgrow="SOMETIMES" />
|
||||||
<children>
|
<ColumnConstraints hgrow="SOMETIMES" percentWidth="90.0" />
|
||||||
<Pane fx:id="specialPane" GridPane.columnIndex="1" />
|
<ColumnConstraints hgrow="SOMETIMES" />
|
||||||
</children>
|
</columnConstraints>
|
||||||
</GridPane>
|
<rowConstraints>
|
||||||
<TextArea fx:id="logArea" editable="false" VBox.vgrow="ALWAYS">
|
<RowConstraints vgrow="SOMETIMES" />
|
||||||
<VBox.margin>
|
</rowConstraints>
|
||||||
<Insets bottom="2.0" left="5.0" right="5.0" top="5.0" />
|
<children>
|
||||||
</VBox.margin>
|
<Pane fx:id="specialPane" GridPane.columnIndex="1" />
|
||||||
</TextArea>
|
</children>
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="2.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</GridPane>
|
||||||
|
<fx:include fx:id="tableFilesList" source="TableView.fxml" VBox.vgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
<graphic>
|
||||||
|
<SVGPath content="M21,19V17H8V19H21M21,13V11H8V13H21M8,7H21V5H8V7M4,5V7H6V5H4M3,5A1,1 0 0,1 4,4H6A1,1 0 0,1 7,5V7A1,1 0 0,1 6,8H4A1,1 0 0,1 3,7V5M4,11V13H6V11H4M3,11A1,1 0 0,1 4,10H6A1,1 0 0,1 7,11V13A1,1 0 0,1 6,14H4A1,1 0 0,1 3,13V11M4,17V19H6V17H4M3,17A1,1 0 0,1 4,16H6A1,1 0 0,1 7,17V19A1,1 0 0,1 6,20H4A1,1 0 0,1 3,19V17Z" />
|
||||||
|
</graphic>
|
||||||
|
</Tab>
|
||||||
|
<Tab closable="false">
|
||||||
|
<content>
|
||||||
|
<VBox prefHeight="200.0" prefWidth="100.0">
|
||||||
|
<children>
|
||||||
|
<TextArea fx:id="logArea" editable="false" VBox.vgrow="ALWAYS">
|
||||||
|
<VBox.margin>
|
||||||
|
<Insets bottom="2.0" left="5.0" right="5.0" top="5.0" />
|
||||||
|
</VBox.margin>
|
||||||
|
</TextArea>
|
||||||
|
</children>
|
||||||
|
</VBox>
|
||||||
|
</content>
|
||||||
|
<graphic>
|
||||||
|
<SVGPath content="M19,3H14.82C14.4,1.84 13.3,1 12,1C10.7,1 9.6,1.84 9.18,3H5A2,2 0 0,0 3,5V19A2,2 0 0,0 5,21H19A2,2 0 0,0 21,19V5A2,2 0 0,0 19,3M12,3A1,1 0 0,1 13,4A1,1 0 0,1 12,5A1,1 0 0,1 11,4A1,1 0 0,1 12,3M7,7H17V5H19V19H5V5H7V7Z" />
|
||||||
|
</graphic>
|
||||||
|
</Tab>
|
||||||
|
</tabs>
|
||||||
|
</TabPane>
|
||||||
<ProgressBar fx:id="progressBar" prefWidth="Infinity" progress="0.0">
|
<ProgressBar fx:id="progressBar" prefWidth="Infinity" progress="0.0">
|
||||||
<VBox.margin>
|
<VBox.margin>
|
||||||
<Insets left="5.0" right="5.0" top="2.0" />
|
<Insets left="5.0" right="5.0" top="2.0" />
|
||||||
|
|
14
src/main/resources/TableView.fxml
Normal file
14
src/main/resources/TableView.fxml
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
|
||||||
|
<?import javafx.geometry.Insets?>
|
||||||
|
<?import javafx.scene.control.TableView?>
|
||||||
|
<?import javafx.scene.layout.HBox?>
|
||||||
|
|
||||||
|
<HBox xmlns="http://javafx.com/javafx/8.0.141" xmlns:fx="http://javafx.com/fxml/1" fx:controller="nsusbloader.Controllers.NSTableViewController">
|
||||||
|
<children>
|
||||||
|
<TableView fx:id="table" editable="true" HBox.hgrow="ALWAYS" />
|
||||||
|
</children>
|
||||||
|
<padding>
|
||||||
|
<Insets bottom="2.0" left="5.0" right="5.0" top="5.0" />
|
||||||
|
</padding>
|
||||||
|
</HBox>
|
|
@ -120,6 +120,46 @@
|
||||||
// Text color of focused item in the list
|
// Text color of focused item in the list
|
||||||
.choice-box .menu-item:focused > .label { -fx-text-fill: #2d2d2d; }
|
.choice-box .menu-item:focused > .label { -fx-text-fill: #2d2d2d; }
|
||||||
|
|
||||||
|
.tab-pane .tab SVGPath{
|
||||||
|
-fx-fill: #66d053;
|
||||||
|
|
||||||
|
}
|
||||||
|
.tab-pane .tab{
|
||||||
|
-fx-background-color: #4f4f4f;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane .tab:selected{
|
||||||
|
-fx-background-color: #2d2d2d;
|
||||||
|
}
|
||||||
|
.table-view {
|
||||||
|
-fx-background-color: #2d2d2d;
|
||||||
|
-fx-background-image: url(app_logo.png);
|
||||||
|
-fx-background-position: center;
|
||||||
|
-fx-background-repeat: no-repeat;
|
||||||
|
-fx-border-color: #00ffc9;
|
||||||
|
-fx-border-radius: 3;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
}
|
||||||
|
.table-view .column-header {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
.table-view .column-header-background .label{
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
.table-view .column-header-background, .table-view .filler{
|
||||||
|
-fx-background-color: #4f4f4f;
|
||||||
|
}
|
||||||
|
.table-row-cell:filled:selected {
|
||||||
|
-fx-background: -fx-control-inner-background ;
|
||||||
|
-fx-background-color: -fx-table-cell-border-color, -fx-background ;
|
||||||
|
-fx-background-insets: 0, 0 0 1 0 ;
|
||||||
|
-fx-table-cell-border-color: derive(-fx-color, 5%);
|
||||||
|
}
|
||||||
|
.table-row-cell:odd:filled:selected {
|
||||||
|
-fx-background: -fx-control-inner-background-alt ;
|
||||||
|
}
|
||||||
|
|
||||||
.regionUpload{
|
.regionUpload{
|
||||||
-fx-shape: "M8,21V19H16V21H8M8,17V15H16V17H8M8,13V11H16V13H8M19,9H5L12,2L19,9Z";
|
-fx-shape: "M8,21V19H16V21H8M8,17V15H16V17H8M8,13V11H16V13H8M19,9H5L12,2L19,9Z";
|
||||||
-fx-background-color: #a2e019;
|
-fx-background-color: #a2e019;
|
||||||
|
|
|
@ -121,6 +121,47 @@
|
||||||
// Text color of focused item in the list
|
// Text color of focused item in the list
|
||||||
.choice-box .menu-item:focused > .label { -fx-text-fill: #2d2d2d; }
|
.choice-box .menu-item:focused > .label { -fx-text-fill: #2d2d2d; }
|
||||||
|
|
||||||
|
.tab-pane .tab SVGPath{
|
||||||
|
-fx-fill: #2c2c2c;
|
||||||
|
|
||||||
|
}
|
||||||
|
.tab-pane .tab{
|
||||||
|
-fx-background-color: #fefefe;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tab-pane .tab:selected{
|
||||||
|
-fx-background-color: #ebebeb;
|
||||||
|
}
|
||||||
|
.table-view {
|
||||||
|
-fx-background-color: #fefefe;
|
||||||
|
-fx-background-image: url(app_logo.png);
|
||||||
|
-fx-background-position: center;
|
||||||
|
-fx-background-repeat: no-repeat;
|
||||||
|
-fx-border-color: #00ffc9;
|
||||||
|
-fx-border-radius: 3;
|
||||||
|
-fx-border-width: 2;
|
||||||
|
}
|
||||||
|
.table-view .column-header {
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
}
|
||||||
|
.table-view .column-header-background .label{
|
||||||
|
-fx-background-color: transparent;
|
||||||
|
-fx-text-fill: white;
|
||||||
|
}
|
||||||
|
.table-view .column-header-background, .table-view .filler{
|
||||||
|
-fx-background-color: #fefefe;
|
||||||
|
}
|
||||||
|
// Remove visible selection 1
|
||||||
|
.table-row-cell:filled:selected {
|
||||||
|
-fx-background: -fx-control-inner-background ;
|
||||||
|
-fx-background-color: -fx-table-cell-border-color, -fx-background ;
|
||||||
|
-fx-background-insets: 0, 0 0 1 0 ;
|
||||||
|
-fx-table-cell-border-color: derive(-fx-color, 5%);
|
||||||
|
}
|
||||||
|
// Remove visible selection 2
|
||||||
|
.table-row-cell:odd:filled:selected {
|
||||||
|
-fx-background: -fx-control-inner-background-alt ;
|
||||||
|
}
|
||||||
.regionUpload{
|
.regionUpload{
|
||||||
-fx-shape: "M8,21V19H16V21H8M8,17V15H16V17H8M8,13V11H16V13H8M19,9H5L12,2L19,9Z";
|
-fx-shape: "M8,21V19H16V21H8M8,17V15H16V17H8M8,13V11H16V13H8M19,9H5L12,2L19,9Z";
|
||||||
-fx-background-color: #a2e019;
|
-fx-background-color: #a2e019;
|
||||||
|
@ -128,6 +169,7 @@
|
||||||
-fx-min-height: -size;
|
-fx-min-height: -size;
|
||||||
-fx-min-width: 20;
|
-fx-min-width: 20;
|
||||||
}
|
}
|
||||||
|
|
||||||
.regionStop{
|
.regionStop{
|
||||||
-fx-shape: "M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z";
|
-fx-shape: "M12,2C17.53,2 22,6.47 22,12C22,17.53 17.53,22 12,22C6.47,22 2,17.53 2,12C2,6.47 6.47,2 12,2M15.59,7L12,10.59L8.41,7L7,8.41L10.59,12L7,15.59L8.41,17L12,13.41L15.59,17L17,15.59L13.41,12L17,8.41L15.59,7Z";
|
||||||
-fx-background-color: #fb582c;
|
-fx-background-color: #fb582c;
|
||||||
|
|
BIN
src/main/resources/res/app_logo.png
Normal file
BIN
src/main/resources/res/app_logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
Loading…
Reference in a new issue