Typos, minor fixes, new locale/resourceBundle support, code-refactoring

This commit is contained in:
Dmitry Isaenko 2019-03-14 21:28:20 +03:00
parent bda316abbb
commit 00db6984d9
14 changed files with 296 additions and 324 deletions

View file

@ -35,16 +35,20 @@ JRE 8u60 or higher. See below.
### macOS ### macOS
See 'Linux' section. Double-click on downloaded .jar file. Follow instructions. Or see 'Linux' section.
Set 'Security & Privacy' settings if needed. Set 'Security & Privacy' settings if needed.
If you use different MacOS (not Mojave) - check release section for another JAR file.
### Windows: ### Windows:
* Download Zadig: https://zadig.akeo.ie/ * Download Zadig: https://zadig.akeo.ie/
* Open tinfoil. Set 'Title Managment' -> 'Usb install NSP' * Open tinfoil. Set 'Title Management' -> 'Usb install NSP'
* Connect NS to PC * 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 * 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+) * 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`) * Get this application (JAR file) double-click on on it (alternatively open 'cmd', go to place where jar located and execute via `java -jar thisAppName.jar`)
@ -66,10 +70,10 @@ Table 'Status' = 'Uploaded' does not means that file installed. It means that it
Handling successful/failed installation is a purpose of the other side application (TinFoil/GoldLeaf). (And they don't provide any feedback interfaces so I can't detect success/failure.) 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: ## TODO:
- [x] macOS QA v0.1 - [x] macOS QA v0.1 (Mojave)
- [ ] macOS QA v0.2 (partly) - [x] macOS QA v0.2.2 (Mojave)
- [x] Windows support - [x] Windows support
- [ ] code refactoring (almost. todo: printLog() ) - [x] code refactoring
- [x] GoldLeaf support - [x] GoldLeaf support
- [ ] XCI support - [ ] XCI support
- [ ] File order sort (non-critical) - [ ] File order sort (non-critical)

View file

@ -8,7 +8,7 @@
<name>NS-USBloader</name> <name>NS-USBloader</name>
<artifactId>ns-usbloader</artifactId> <artifactId>ns-usbloader</artifactId>
<version>0.2.3_DEV-SNAPSHOT</version> <version>0.3_DEV-SNAPSHOT</version>
<url>https://github.com/developersu/ns-usbloader/</url> <url>https://github.com/developersu/ns-usbloader/</url>
<description> <description>
@ -140,7 +140,7 @@
<dependency> <dependency>
<groupId>org.usb4java</groupId> <groupId>org.usb4java</groupId>
<artifactId>usb4java</artifactId> <artifactId>usb4java</artifactId>
<version>1.2.0</version> <version>1.3.0</version>
<scope>compile</scope> <scope>compile</scope>
</dependency> </dependency>
</dependencies> </dependencies>

View file

@ -11,7 +11,7 @@ import javafx.stage.FileChooser;
import nsusbloader.AppPreferences; import nsusbloader.AppPreferences;
import nsusbloader.MediatorControl; import nsusbloader.MediatorControl;
import nsusbloader.NSLMain; import nsusbloader.NSLMain;
import nsusbloader.UsbCommunications; import nsusbloader.USB.UsbCommunications;
import java.io.File; import java.io.File;
import java.net.URL; import java.net.URL;
@ -137,7 +137,7 @@ public class NSLMainController implements Initializable {
if (usbThread == null || !usbThread.isAlive()){ if (usbThread == null || !usbThread.isAlive()){
List<File> nspToUpload; List<File> nspToUpload;
if ((nspToUpload = tableFilesListController.getFiles()) == null) { if ((nspToUpload = tableFilesListController.getFiles()) == null) {
resourceBundle.getString("logsNoFolderFileSelected"); logArea.setText(resourceBundle.getString("logsNoFolderFileSelected"));
return; return;
}else { }else {
logArea.setText(resourceBundle.getString("logsFilesToUploadTitle")+"\n"); logArea.setText(resourceBundle.getString("logsFilesToUploadTitle")+"\n");

View file

@ -18,7 +18,7 @@ public class MediatorControl {
public void setController(NSLMainController controller){ public void setController(NSLMainController controller){
this.applicationController = controller; this.applicationController = controller;
} }
NSLMainController getContoller(){ return this.applicationController; } public NSLMainController getContoller(){ return this.applicationController; }
public synchronized void setTransferActive(boolean state) { public synchronized void setTransferActive(boolean state) {
isTransferActive.set(state); isTransferActive.set(state);

View file

@ -12,17 +12,16 @@ import java.util.Locale;
import java.util.ResourceBundle; import java.util.ResourceBundle;
public class NSLMain extends Application { public class NSLMain extends Application {
public static final String appVersion = "v0.2.3_DEV"; public static final String appVersion = "v0.3_DEV";
@Override @Override
public void start(Stage primaryStage) throws Exception{ public void start(Stage primaryStage) throws Exception{
FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml")); FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml"));
ResourceBundle rb; ResourceBundle rb;
if (Locale.getDefault().getISO3Language().equals("rus")) Locale userLocale = new Locale(Locale.getDefault().getISO3Language()); // NOTE: user locale based on ISO3 Language codes
rb = ResourceBundle.getBundle("locale", new Locale("ru")); rb = ResourceBundle.getBundle("locale", userLocale);
else
rb = ResourceBundle.getBundle("locale", new Locale("en"));
loader.setResources(rb); loader.setResources(rb);
Parent root = loader.load(); Parent root = loader.load();

View file

@ -0,0 +1,63 @@
package nsusbloader.USB;
import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.NSLDataTypes.EMsgType;
import java.io.File;
import java.util.HashMap;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
public class LogPrinter {
private MessagesConsumer msgConsumer;
private BlockingQueue<String> msgQueue;
private BlockingQueue<Double> progressQueue;
private HashMap<String, EFileStatus> statusMap; // BlockingQueue for literally one object. TODO: read more books ; replace to hashMap
LogPrinter(){
this.msgQueue = new LinkedBlockingQueue<>();
this.progressQueue = new LinkedBlockingQueue<>();
this.statusMap = new HashMap<>();
this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue, this.statusMap);
this.msgConsumer.start();
}
/**
* This is what will print to textArea of the application.
* */
public void print(String message, EMsgType type){
try {
switch (type){
case PASS:
msgQueue.put("[ PASS ] "+message+"\n");
break;
case FAIL:
msgQueue.put("[ FAIL ] "+message+"\n");
break;
case INFO:
msgQueue.put("[ INFO ] "+message+"\n");
break;
case WARNING:
msgQueue.put("[ WARN ] "+message+"\n");
break;
default:
msgQueue.put(message);
}
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
/**
* Update progress for progress bar
* */
public void updateProgress(Double value) throws InterruptedException{
progressQueue.put(value);
}
/**
* When we're done
* */
public void updateAndClose(HashMap<String, File> nspMap, EFileStatus status){
for (String fileName: nspMap.keySet())
statusMap.put(fileName, status);
msgConsumer.interrupt();
}
}

View file

@ -1,10 +1,11 @@
package nsusbloader; package nsusbloader.USB;
import javafx.animation.AnimationTimer; import javafx.animation.AnimationTimer;
import javafx.scene.control.ProgressBar; import javafx.scene.control.ProgressBar;
import javafx.scene.control.ProgressIndicator; import javafx.scene.control.ProgressIndicator;
import javafx.scene.control.TextArea; import javafx.scene.control.TextArea;
import nsusbloader.Controllers.NSTableViewController; import nsusbloader.Controllers.NSTableViewController;
import nsusbloader.MediatorControl;
import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.NSLDataTypes.EFileStatus;
import java.util.ArrayList; import java.util.ArrayList;
@ -69,7 +70,7 @@ public class MessagesConsumer extends AnimationTimer {
} }
} }
void interrupt(){ public void interrupt(){
this.isInterrupted = true; this.isInterrupted = true;
} }
} }

View file

@ -1,4 +1,4 @@
package nsusbloader.PFS; package nsusbloader.USB.PFS;
import java.nio.ByteBuffer; import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;

View file

@ -1,5 +1,6 @@
package nsusbloader.PFS; package nsusbloader.USB.PFS;
import nsusbloader.USB.LogPrinter;
import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.ServiceWindow; import nsusbloader.ServiceWindow;
@ -8,15 +9,14 @@ import java.nio.ByteBuffer;
import java.nio.ByteOrder; import java.nio.ByteOrder;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.*; import java.util.*;
import java.util.concurrent.BlockingQueue;
/** /**
* Used in GoldLeaf USB protocol * Used in GoldLeaf USB protocol
* */ * */
public class PFSProvider { 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 LogPrinter logPrinter;
private ResourceBundle rb; private ResourceBundle rb;
private RandomAccessFile randAccessFile; private RandomAccessFile randAccessFile;
@ -25,20 +25,18 @@ public class PFSProvider {
private long bodySize; private long bodySize;
private int ticketID = -1; private int ticketID = -1;
public PFSProvider(File nspFile, BlockingQueue msgQueue){ public PFSProvider(File nspFile, LogPrinter logPrinter){
this.msgQueue = msgQueue; this.logPrinter = logPrinter;
try { try {
this.randAccessFile = new RandomAccessFile(nspFile, "r"); this.randAccessFile = new RandomAccessFile(nspFile, "r");
nspFileName = nspFile.getName(); nspFileName = nspFile.getName();
} }
catch (FileNotFoundException fnfe){ catch (FileNotFoundException fnfe){
printLog("PFS File not founnd: \n "+fnfe.getMessage(), EMsgType.FAIL); logPrinter.print("PFS File not founnd: \n "+fnfe.getMessage(), EMsgType.FAIL);
nspFileName = null; nspFileName = null;
} }
if (Locale.getDefault().getISO3Language().equals("rus")) Locale userLocale = new Locale(Locale.getDefault().getISO3Language());
rb = ResourceBundle.getBundle("locale", new Locale("ru")); rb = ResourceBundle.getBundle("locale", userLocale);
else
rb = ResourceBundle.getBundle("locale", new Locale("en"));
} }
public boolean init() { public boolean init() {
@ -48,22 +46,22 @@ public class PFSProvider {
int filesCount; int filesCount;
int header; int header;
printLog("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO); logPrinter.print("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO);
try { try {
byte[] fileStartingBytes = new byte[12]; byte[] fileStartingBytes = new byte[12];
// Read PFS0, files count, header, padding (4 zero bytes) // Read PFS0, files count, header, padding (4 zero bytes)
if (randAccessFile.read(fileStartingBytes) == 12) if (randAccessFile.read(fileStartingBytes) == 12)
printLog("PFS Read file starting bytes.", EMsgType.PASS); logPrinter.print("PFS Read file starting bytes.", EMsgType.PASS);
else { else {
printLog("PFS Read file starting bytes.", EMsgType.FAIL); logPrinter.print("PFS Read file starting bytes.", EMsgType.FAIL);
randAccessFile.close(); randAccessFile.close();
return false; return false;
} }
// Check PFS0 // Check PFS0
if (Arrays.equals(PFS0, Arrays.copyOfRange(fileStartingBytes, 0, 4))) if (Arrays.equals(PFS0, Arrays.copyOfRange(fileStartingBytes, 0, 4)))
printLog("PFS Read 'PFS0'.", EMsgType.PASS); logPrinter.print("PFS Read 'PFS0'.", EMsgType.PASS);
else { else {
printLog("PFS Read 'PFS0'.", EMsgType.WARNING); logPrinter.print("PFS Read 'PFS0'.", EMsgType.WARNING);
if (!ServiceWindow.getConfirmationWindow(nspFileName+"\n"+rb.getString("windowTitleConfirmWrongPFS0"), rb.getString("windowBodyConfirmWrongPFS0"))) { if (!ServiceWindow.getConfirmationWindow(nspFileName+"\n"+rb.getString("windowTitleConfirmWrongPFS0"), rb.getString("windowBodyConfirmWrongPFS0"))) {
randAccessFile.close(); randAccessFile.close();
return false; return false;
@ -72,19 +70,19 @@ public class PFSProvider {
// Get files count // Get files count
filesCount = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 4, 8)).order(ByteOrder.LITTLE_ENDIAN).getInt(); filesCount = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 4, 8)).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (filesCount > 0 ) { if (filesCount > 0 ) {
printLog("PFS Read files count [" + filesCount + "]", EMsgType.PASS); logPrinter.print("PFS Read files count [" + filesCount + "]", EMsgType.PASS);
} }
else { else {
printLog("PFS Read files count", EMsgType.FAIL); logPrinter.print("PFS Read files count", EMsgType.FAIL);
randAccessFile.close(); randAccessFile.close();
return false; return false;
} }
// Get header // Get header
header = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 8, 12)).order(ByteOrder.LITTLE_ENDIAN).getInt(); header = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 8, 12)).order(ByteOrder.LITTLE_ENDIAN).getInt();
if (header > 0 ) if (header > 0 )
printLog("PFS Read header ["+header+"]", EMsgType.PASS); logPrinter.print("PFS Read header ["+header+"]", EMsgType.PASS);
else { else {
printLog("PFS Read header ", EMsgType.FAIL); logPrinter.print("PFS Read header ", EMsgType.FAIL);
randAccessFile.close(); randAccessFile.close();
return false; return false;
} }
@ -103,10 +101,10 @@ 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("PFS Read NCA inside NSP: " + i, EMsgType.PASS); logPrinter.print("PFS Read NCA inside NSP: " + i, EMsgType.PASS);
} }
else { else {
printLog("PFS Read NCA inside NSP: "+i, EMsgType.FAIL); logPrinter.print("PFS Read NCA inside NSP: "+i, EMsgType.FAIL);
randAccessFile.close(); randAccessFile.close();
return false; return false;
} }
@ -115,10 +113,10 @@ public class PFSProvider {
nca_size = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 12, 20)).order(ByteOrder.LITTLE_ENDIAN).getLong(); nca_size = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 12, 20)).order(ByteOrder.LITTLE_ENDIAN).getLong();
nca_name_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 20, 24)).order(ByteOrder.LITTLE_ENDIAN).getInt(); // yes, cast from int to long. nca_name_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 20, 24)).order(ByteOrder.LITTLE_ENDIAN).getInt(); // yes, cast from int to long.
printLog(" Padding check", offset == 0?EMsgType.PASS:EMsgType.WARNING); logPrinter.print(" Padding check", offset == 0?EMsgType.PASS:EMsgType.WARNING);
printLog(" NCA offset check: "+nca_offset, nca_offset >= 0?EMsgType.PASS:EMsgType.WARNING); logPrinter.print(" NCA offset check: "+nca_offset, nca_offset >= 0?EMsgType.PASS:EMsgType.WARNING);
printLog(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING); logPrinter.print(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING);
printLog(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING); logPrinter.print(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING);
NCAFile ncaFile = new NCAFile(); NCAFile ncaFile = new NCAFile();
ncaFile.setNcaOffset(nca_offset); ncaFile.setNcaOffset(nca_offset);
@ -130,15 +128,15 @@ public class PFSProvider {
// Final offset // Final offset
byte[] bufForInt = new byte[4]; byte[] bufForInt = new byte[4];
if ((randAccessFile.read(bufForInt) == 4) && (Arrays.equals(bufForInt, new byte[4]))) if ((randAccessFile.read(bufForInt) == 4) && (Arrays.equals(bufForInt, new byte[4])))
printLog("PFS Final padding check", EMsgType.PASS); logPrinter.print("PFS Final padding check", EMsgType.PASS);
else else
printLog("PFS Final padding check", EMsgType.WARNING); logPrinter.print("PFS Final padding check", EMsgType.WARNING);
// Calculate position including header for body size offset // Calculate position including header for body size offset
bodySize = randAccessFile.getFilePointer()+header; bodySize = randAccessFile.getFilePointer()+header;
//********************************************************************************************* //*********************************************************************************************
// Collect file names from NCAs // Collect file names from NCAs
printLog("PFS Collecting file names", EMsgType.INFO); logPrinter.print("PFS Collecting file names", EMsgType.INFO);
List<Byte> ncaFN; // Temporary List<Byte> ncaFN; // Temporary
byte[] b = new byte[1]; // Temporary byte[] b = new byte[1]; // Temporary
for (int i=0; i<filesCount; i++){ for (int i=0; i<filesCount; i++){
@ -161,10 +159,10 @@ public class PFSProvider {
randAccessFile.close(); randAccessFile.close();
} }
catch (IOException ioe){ catch (IOException ioe){
printLog("PFS Failed NSP file analyze for ["+nspFileName+"]\n "+ioe.getMessage(), EMsgType.FAIL); logPrinter.print("PFS Failed NSP file analyze for ["+nspFileName+"]\n "+ioe.getMessage(), EMsgType.FAIL);
ioe.printStackTrace(); ioe.printStackTrace();
} }
printLog("PFS Finish NSP file analyze for ["+nspFileName+"]", EMsgType.PASS); logPrinter.print("PFS Finish NSP file analyze for ["+nspFileName+"]", EMsgType.PASS);
return true; return true;
} }
@ -217,27 +215,4 @@ public class PFSProvider {
public int getNcaTicketID(){ public int getNcaTicketID(){
return ticketID; return ticketID;
} }
/**
* This is what will print to textArea of the application.
**/
private void printLog(String message, EMsgType type){
try {
switch (type){
case PASS:
msgQueue.put("[ PASS ] "+message+"\n");
break;
case FAIL:
msgQueue.put("[ FAIL ] "+message+"\n");
break;
case INFO:
msgQueue.put("[ INFO ] "+message+"\n");
break;
case WARNING:
msgQueue.put("[ WARN ] "+message+"\n");
break;
}
}catch (InterruptedException ie){
ie.printStackTrace(); //TODO: ???
}
}
} }

View file

@ -1,9 +1,9 @@
package nsusbloader; package nsusbloader.USB;
import javafx.concurrent.Task; import javafx.concurrent.Task;
import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.NSLDataTypes.EFileStatus;
import nsusbloader.NSLDataTypes.EMsgType; import nsusbloader.NSLDataTypes.EMsgType;
import nsusbloader.PFS.PFSProvider; import nsusbloader.USB.PFS.PFSProvider;
import org.usb4java.*; import org.usb4java.*;
import java.io.*; import java.io.*;
@ -15,21 +15,13 @@ import java.nio.charset.StandardCharsets;
import java.util.Arrays; import java.util.Arrays;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.LinkedBlockingQueue;
import static nsusbloader.RainbowHexDump.hexDumpUTF8;
public class UsbCommunications extends Task<Void> { public class UsbCommunications extends Task<Void> {
private final int DEFAULT_INTERFACE = 0; private final int DEFAULT_INTERFACE = 0;
private BlockingQueue<String> msgQueue; private LogPrinter logPrinter;
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 EFileStatus status = EFileStatus.FAILED;
private MessagesConsumer msgConsumer;
private HashMap<String, File> nspMap; private HashMap<String, File> nspMap;
private Context contextNS; private Context contextNS;
@ -52,39 +44,35 @@ public class UsbCommunications extends Task<Void> {
this.nspMap = new HashMap<>(); this.nspMap = new HashMap<>();
for (File f: nspList) for (File f: nspList)
nspMap.put(f.getName(), f); nspMap.put(f.getName(), f);
this.msgQueue = new LinkedBlockingQueue<>(); this.logPrinter = new LogPrinter();
this.progressQueue = new LinkedBlockingQueue<>();
this.statusMap = new HashMap<>();
this.msgConsumer = new MessagesConsumer(this.msgQueue, this.progressQueue, this.statusMap);
} }
@Override @Override
protected Void call() { protected Void call() {
this.msgConsumer.start();
int result = -9999; int result = -9999;
printLog("\tStart chain", EMsgType.INFO); logPrinter.print("\tStart chain", EMsgType.INFO);
// Creating Context required by libusb. Optional. TODO: Consider removing. // Creating Context required by libusb. Optional. TODO: Consider removing.
contextNS = new Context(); contextNS = new Context();
result = LibUsb.init(contextNS); result = LibUsb.init(contextNS);
if (result != LibUsb.SUCCESS) { if (result != LibUsb.SUCCESS) {
printLog("libusb initialization\n Returned: "+result, EMsgType.FAIL); logPrinter.print("libusb initialization\n Returned: "+result, EMsgType.FAIL);
close(); close();
return null; return null;
} }
else else
printLog("libusb initialization", EMsgType.PASS); logPrinter.print("libusb initialization", EMsgType.PASS);
// Searching for NS in devices: obtain list of all devices // Searching for NS in devices: obtain list of all devices
DeviceList deviceList = new DeviceList(); DeviceList deviceList = new DeviceList();
result = LibUsb.getDeviceList(contextNS, deviceList); result = LibUsb.getDeviceList(contextNS, deviceList);
if (result < 0) { if (result < 0) {
printLog("Get device list\n Returned: "+result, EMsgType.FAIL); logPrinter.print("Get device list\n Returned: "+result, EMsgType.FAIL);
close(); close();
return null; return null;
} }
else { else {
printLog("Get device list", EMsgType.PASS); logPrinter.print("Get device list", EMsgType.PASS);
} }
// Searching for NS in devices: looking for NS // Searching for NS in devices: looking for NS
DeviceDescriptor descriptor; DeviceDescriptor descriptor;
@ -93,14 +81,14 @@ public class UsbCommunications extends Task<Void> {
descriptor = new DeviceDescriptor(); // mmm.. leave it as is. descriptor = new DeviceDescriptor(); // mmm.. leave it as is.
result = LibUsb.getDeviceDescriptor(device, descriptor); result = LibUsb.getDeviceDescriptor(device, descriptor);
if (result != LibUsb.SUCCESS){ if (result != LibUsb.SUCCESS){
printLog("Read file descriptors for USB devices\n Returned: "+result, EMsgType.FAIL); logPrinter.print("Read file descriptors for USB devices\n Returned: "+result, EMsgType.FAIL);
LibUsb.freeDeviceList(deviceList, true); LibUsb.freeDeviceList(deviceList, true);
close(); close();
return null; return null;
} }
if ((descriptor.idVendor() == 0x057E) && descriptor.idProduct() == 0x3000){ if ((descriptor.idVendor() == 0x057E) && descriptor.idProduct() == 0x3000){
deviceNS = device; deviceNS = device;
printLog("Read file descriptors for USB devices", EMsgType.PASS); logPrinter.print("Read file descriptors for USB devices", EMsgType.PASS);
break; break;
} }
} }
@ -114,13 +102,13 @@ public class UsbCommunications extends Task<Void> {
switch (result){ switch (result){
case 0: case 0:
printLog("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS); logPrinter.print("DBG: getActiveConfigDescriptor\n"+configDescriptor.dump(), EMsgType.PASS);
break; break;
case LibUsb.ERROR_NOT_FOUND: case LibUsb.ERROR_NOT_FOUND:
printLog("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL); logPrinter.print("DBG: getActiveConfigDescriptor: ERROR_NOT_FOUND", EMsgType.FAIL);
break; break;
default: default:
printLog("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL); logPrinter.print("DBG: getActiveConfigDescriptor: "+result, EMsgType.FAIL);
break; break;
} }
@ -141,10 +129,10 @@ public class UsbCommunications extends Task<Void> {
////////////////////////////////////////// DEBUG INFORMATION END ///////////////////////////////////////////// ////////////////////////////////////////// DEBUG INFORMATION END /////////////////////////////////////////////
if (deviceNS != null){ if (deviceNS != null){
printLog("NS in connected USB devices found", EMsgType.PASS); logPrinter.print("NS in connected USB devices found", EMsgType.PASS);
} }
else { else {
printLog("NS in connected USB devices not found", EMsgType.FAIL); logPrinter.print("NS in connected USB devices not found", EMsgType.FAIL);
close(); close();
return null; return null;
} }
@ -152,27 +140,16 @@ public class UsbCommunications extends Task<Void> {
handlerNS = new DeviceHandle(); handlerNS = new DeviceHandle();
result = LibUsb.open(deviceNS, handlerNS); result = LibUsb.open(deviceNS, handlerNS);
if (result != LibUsb.SUCCESS) { if (result != LibUsb.SUCCESS) {
switch (result){ logPrinter.print("Open NS USB device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
case LibUsb.ERROR_ACCESS: if (result == LibUsb.ERROR_ACCESS)
printLog("Open NS USB device\n Returned: ERROR_ACCESS", EMsgType.FAIL); logPrinter.print("Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!", EMsgType.INFO);
printLog("Double check that you have administrator privileges (you're 'root') or check 'udev' rules set for this user (linux only)!", EMsgType.INFO);
break;
case LibUsb.ERROR_NO_MEM:
printLog("Open NS USB device\n Returned: ERROR_NO_MEM", EMsgType.FAIL);
break;
case LibUsb.ERROR_NO_DEVICE:
printLog("Open NS USB device\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
default:
printLog("Open NS USB device\n Returned:" + result, EMsgType.FAIL);
}
close(); close();
return null; return null;
} }
else else
printLog("Open NS USB device", EMsgType.PASS); logPrinter.print("Open NS USB device", EMsgType.PASS);
printLog("Free device list", EMsgType.INFO); logPrinter.print("Free device list", EMsgType.INFO);
LibUsb.freeDeviceList(deviceList, true); LibUsb.freeDeviceList(deviceList, true);
// DO some stuff to connected NS // DO some stuff to connected NS
@ -180,99 +157,55 @@ public class UsbCommunications extends Task<Void> {
boolean canDetach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER); // if cant, it's windows ot old lib boolean canDetach = LibUsb.hasCapability(LibUsb.CAP_SUPPORTS_DETACH_KERNEL_DRIVER); // if cant, it's windows ot old lib
if (canDetach){ if (canDetach){
int usedByKernel = LibUsb.kernelDriverActive(handlerNS, DEFAULT_INTERFACE); int usedByKernel = LibUsb.kernelDriverActive(handlerNS, DEFAULT_INTERFACE);
if (usedByKernel == LibUsb.SUCCESS){ if (usedByKernel == LibUsb.SUCCESS){
printLog("Can proceed with libusb driver", EMsgType.PASS); // we're good logPrinter.print("Can proceed with libusb driver", EMsgType.PASS); // we're good
} }
else { else if (usedByKernel == 1) { // used by kernel
switch (usedByKernel){ result = LibUsb.detachKernelDriver(handlerNS, DEFAULT_INTERFACE);
case 1: // used by kernel logPrinter.print("Detach kernel required", EMsgType.INFO);
result = LibUsb.detachKernelDriver(handlerNS, DEFAULT_INTERFACE); if (result != 0) {
printLog("Detach kernel required", EMsgType.INFO); logPrinter.print("Detach kernel\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
if (result != 0) { close();
switch (result){ return null;
case LibUsb.ERROR_NOT_FOUND: } else
printLog("Detach kernel\n Returned: ERROR_NOT_FOUND", EMsgType.FAIL); logPrinter.print("Detach kernel", EMsgType.PASS);
break; }
case LibUsb.ERROR_INVALID_PARAM: else
printLog("Detach kernel\n Returned: ERROR_INVALID_PARAM", EMsgType.FAIL); logPrinter.print("Can't proceed with libusb driver\n Returned: "+UsbErrorCodes.getErrCode(usedByKernel), EMsgType.FAIL);
break;
case LibUsb.ERROR_NO_DEVICE:
printLog("Detach kernel\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
case LibUsb.ERROR_NOT_SUPPORTED: // Should never appear only if libusb buggy
printLog("Detach kernel\n Returned: ERROR_NOT_SUPPORTED", EMsgType.FAIL);
break;
default:
printLog("Detach kernel\n Returned: " + result, EMsgType.FAIL);
break;
}
close();
return null;
}
else {
printLog("Detach kernel", EMsgType.PASS);
break;
}
case LibUsb.ERROR_NO_DEVICE:
printLog("Can't proceed with libusb driver\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
case LibUsb.ERROR_NOT_SUPPORTED:
printLog("Can't proceed with libusb driver\n Returned: ERROR_NOT_SUPPORTED", EMsgType.FAIL);
break;
default:
printLog("Can't proceed with libusb driver\n Returned: "+result, EMsgType.FAIL);
}
}
} }
else else
printLog("libusb doesn't supports function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. Proceeding.", EMsgType.WARNING); logPrinter.print("libusb doesn't support function 'CAP_SUPPORTS_DETACH_KERNEL_DRIVER'. It's normal. Proceeding.", EMsgType.WARNING);
/* /*
// Reset device // Reset device
result = LibUsb.resetDevice(handlerNS); result = LibUsb.resetDevice(handlerNS);
if (result == 0) if (result == 0)
printLog("Reset device", EMsgType.PASS); logPrinter.print("Reset device", EMsgType.PASS);
else { else {
printLog("Reset device returned: " + result, EMsgType.FAIL); logPrinter.print("Reset device returned: " + result, EMsgType.FAIL);
close(); updateAndClose();
return null; return null;
} }
*/ */
// Set configuration (soft reset if needed) // Set configuration (soft reset if needed)
result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need result = LibUsb.setConfiguration(handlerNS, 1); // 1 - configuration all we need
if (result != LibUsb.SUCCESS){ if (result != LibUsb.SUCCESS){
switch (result){ logPrinter.print("Set active configuration to device\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
case LibUsb.ERROR_NOT_FOUND:
printLog("Set active configuration to device\n Returned: ERROR_NOT_FOUND", EMsgType.FAIL);
break;
case LibUsb.ERROR_BUSY:
printLog("Set active configuration to device\n Returned: ERROR_BUSY", EMsgType.FAIL);
break;
case LibUsb.ERROR_NO_DEVICE:
printLog("Set active configuration to device\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
case LibUsb.ERROR_INVALID_PARAM:
printLog("Set active configuration to device\n Returned: ERROR_INVALID_PARAM", EMsgType.FAIL);
break;
default:
printLog("Set active configuration to device\n Returned: "+result, EMsgType.FAIL);
break;
}
close(); close();
return null; return null;
} }
else { else {
printLog("Set active configuration to device.", EMsgType.PASS); logPrinter.print("Set active configuration to device.", EMsgType.PASS);
} }
// Claim interface // Claim interface
result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE); result = LibUsb.claimInterface(handlerNS, DEFAULT_INTERFACE);
if (result != LibUsb.SUCCESS) { if (result != LibUsb.SUCCESS) {
printLog("Claim interface\n Returned: "+result, EMsgType.FAIL); logPrinter.print("Claim interface\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
close(); close();
return null; return null;
} }
else else
printLog("Claim interface", EMsgType.PASS); logPrinter.print("Claim interface", EMsgType.PASS);
//-------------------------------------------------------------------------------------------------------------- //--------------------------------------------------------------------------------------------------------------
if (protocol.equals("TinFoil")) { if (protocol.equals("TinFoil")) {
@ -282,7 +215,7 @@ public class UsbCommunications extends Task<Void> {
} }
close(); close();
printLog("\tEnd chain", EMsgType.INFO); logPrinter.print("\tEnd chain", EMsgType.INFO);
return null; return null;
} }
/** /**
@ -304,11 +237,11 @@ public class UsbCommunications extends Task<Void> {
// Send list of NSP files: // Send list of NSP files:
// Proceed "TUL0" // Proceed "TUL0"
if (!writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30} if (!writeToUsb("TUL0".getBytes(StandardCharsets.US_ASCII))) { // new byte[]{(byte) 0x54, (byte) 0x55, (byte) 0x76, (byte) 0x30}
printLog("TF Send list of files: handshake", EMsgType.FAIL); logPrinter.print("TF Send list of files: handshake", EMsgType.FAIL);
return false; return false;
} }
else else
printLog("TF Send list of files: handshake", EMsgType.PASS); logPrinter.print("TF Send list of files: handshake", EMsgType.PASS);
//Collect file names //Collect file names
StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder StringBuilder nspListNamesBuilder = new StringBuilder(); // Add every title to one stringBuilder
for(String nspFileName: nspMap.keySet()) { for(String nspFileName: nspMap.keySet()) {
@ -323,24 +256,24 @@ public class UsbCommunications extends Task<Void> {
//byteBuffer.reset(); //byteBuffer.reset();
// Sending NSP list // Sending NSP list
printLog("TF Send list of files", EMsgType.INFO); logPrinter.print("TF Send list of files", EMsgType.INFO);
if (!writeToUsb(nspListSize)) { // size of the list we're going to transfer goes... if (!writeToUsb(nspListSize)) { // size of the list we're going to transfer goes...
printLog(" [send list length]", EMsgType.FAIL); logPrinter.print(" [send list length]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [send list length]", EMsgType.PASS); logPrinter.print(" [send list length]", EMsgType.PASS);
if (!writeToUsb(new byte[8])) { // 8 zero bytes goes... if (!writeToUsb(new byte[8])) { // 8 zero bytes goes...
printLog(" [send padding]", EMsgType.FAIL); logPrinter.print(" [send padding]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [send padding]", EMsgType.PASS); logPrinter.print(" [send padding]", EMsgType.PASS);
if (!writeToUsb(nspListNames)) { // list of the names goes... if (!writeToUsb(nspListNames)) { // list of the names goes...
printLog(" [send list itself]", EMsgType.FAIL); logPrinter.print(" [send list itself]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [send list itself]", EMsgType.PASS); logPrinter.print(" [send list itself]", EMsgType.PASS);
return true; return true;
} }
@ -348,7 +281,7 @@ public class UsbCommunications extends Task<Void> {
* After we sent commands to NS, this chain starts * After we sent commands to NS, this chain starts
* */ * */
private boolean proceedCommands(){ private boolean proceedCommands(){
printLog("TF Awaiting for NS commands.", EMsgType.INFO); logPrinter.print("TF Awaiting for NS commands.", EMsgType.INFO);
/* byte[] magic = new byte[4]; /* 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);
@ -370,11 +303,11 @@ public class UsbCommunications extends Task<Void> {
// 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("TF Received EXIT command. Terminating.", EMsgType.PASS); logPrinter.print("TF Received EXIT command. Terminating.", EMsgType.PASS);
return true; // All interaction with USB device should be ended (expected); return true; // All interaction with USB device should be ended (expected);
} }
else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack). else if ((receivedArray[8] == 0x01) || (receivedArray[8] == 0x02)){ //0x01 - file range; 0x02 unknown bug on backend side (dirty hack).
printLog("TF Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", EMsgType.PASS); logPrinter.print("TF Received FILE_RANGE command. Proceeding: [0x0"+receivedArray[8]+"]", EMsgType.PASS);
/*// We can get in this pocket a length of file name (+32). Why +32? I dunno man.. Do we need this? Definitely not. This app can live without it. /*// 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
@ -414,7 +347,7 @@ public class UsbCommunications extends Task<Void> {
return false; return false;
String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8); String receivedRequestedNSP = new String(receivedArray, StandardCharsets.UTF_8);
printLog("TF Reply to requested file: "+receivedRequestedNSP logPrinter.print("TF Reply to requested file: "+receivedRequestedNSP
+"\n Range Size: "+receivedRangeSize +"\n Range Size: "+receivedRangeSize
+"\n Range Offset: "+receivedRangeOffset, EMsgType.INFO); +"\n Range Offset: "+receivedRangeOffset, EMsgType.INFO);
@ -428,7 +361,7 @@ public class UsbCommunications extends Task<Void> {
byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
int bufferLength; int bufferLength;
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){ if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
printLog("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL); logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
return false; return false;
} }
@ -446,11 +379,11 @@ public class UsbCommunications extends Task<Void> {
if (isProgessBarInitiated){ if (isProgessBarInitiated){
try { try {
if (currentOffset+readPice == receivedRangeOffset){ if (currentOffset+readPice == receivedRangeOffset){
progressQueue.put(1.0); logPrinter.updateProgress(1.0);
isProgessBarInitiated = false; isProgessBarInitiated = false;
} }
else else
progressQueue.put((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0); logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
}catch (InterruptedException ie){ }catch (InterruptedException ie){
getException().printStackTrace(); // TODO: Do something with this getException().printStackTrace(); // TODO: Do something with this
} }
@ -468,28 +401,28 @@ public class UsbCommunications extends Task<Void> {
if (bufferLength != -1){ if (bufferLength != -1){
//write to USB //write to USB
if (!writeToUsb(bufferCurrent)) { if (!writeToUsb(bufferCurrent)) {
printLog("TF Failure during NSP transmission.", EMsgType.FAIL); logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
return false; return false;
} }
currentOffset += readPice; currentOffset += readPice;
} }
else { else {
printLog("TF Reading of stream suddenly ended.", EMsgType.WARNING); logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
return false; return false;
} }
} }
bufferedInStream.close(); bufferedInStream.close();
} catch (FileNotFoundException fnfe){ } catch (FileNotFoundException fnfe){
printLog("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL); logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL);
fnfe.printStackTrace(); fnfe.printStackTrace();
return false; return false;
} catch (IOException ioe){ } catch (IOException ioe){
printLog("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL); logPrinter.print("TF IOException:\n "+ioe.getMessage(), EMsgType.FAIL);
ioe.printStackTrace(); ioe.printStackTrace();
return false; return false;
} catch (ArithmeticException ae){ } catch (ArithmeticException ae){
printLog("TF ArithmeticException (can't cast end offset minus current to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL); logPrinter.print("TF ArithmeticException (can't cast end offset minus current to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL);
ae.printStackTrace(); ae.printStackTrace();
return false; return false;
} }
@ -502,26 +435,26 @@ public class UsbCommunications extends Task<Void> {
* false if failed * 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. private boolean sendResponse(byte[] rangeSize){ // This method as separate function itself for application needed as a cookie in the middle of desert.
printLog("TF Sending response", EMsgType.INFO); logPrinter.print("TF Sending response", EMsgType.INFO);
if (!writeToUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0' if (!writeToUsb(new byte[] { (byte) 0x54, (byte) 0x55, (byte) 0x43, (byte) 0x30, // 'TUC0'
(byte) 0x01, // CMD_TYPE_RESPONSE = 1 (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) 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. (byte) 0x01, (byte) 0x00, (byte) 0x00, (byte) 0x00} ) // Send integer value of '1' in Little-endian format.
){ ){
printLog(" [1/3]", EMsgType.FAIL); logPrinter.print(" [1/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [1/3]", EMsgType.PASS); logPrinter.print(" [1/3]", EMsgType.PASS);
if(!writeToUsb(rangeSize)) { // Send EXACTLY what has been received if(!writeToUsb(rangeSize)) { // Send EXACTLY what has been received
printLog(" [2/3]", EMsgType.FAIL); logPrinter.print(" [2/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [2/3]", EMsgType.PASS); logPrinter.print(" [2/3]", EMsgType.PASS);
if(!writeToUsb(new byte[12])) { // kinda another one padding if(!writeToUsb(new byte[12])) { // kinda another one padding
printLog(" [3/3]", EMsgType.FAIL); logPrinter.print(" [3/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [3/3]", EMsgType.PASS); logPrinter.print(" [3/3]", EMsgType.PASS);
return true; return true;
} }
@ -542,14 +475,14 @@ public class UsbCommunications extends Task<Void> {
private final byte[] CMD_Finish = new byte[]{0x47, 0x4c, 0x55, 0x43, 0x07, 0x00, 0x00, 0x00}; private final byte[] CMD_Finish = new byte[]{0x47, 0x4c, 0x55, 0x43, 0x07, 0x00, 0x00, 0x00};
GoldLeaf(){ GoldLeaf(){
printLog("===========================================================================", EMsgType.INFO); logPrinter.print("===========================================================================", EMsgType.INFO);
PFSProvider pfsElement = new PFSProvider(nspMap.get(nspMap.keySet().toArray()[0]), msgQueue); PFSProvider pfsElement = new PFSProvider(nspMap.get(nspMap.keySet().toArray()[0]), logPrinter);
if (!pfsElement.init()) { if (!pfsElement.init()) {
printLog("GL File provided have incorrect structure and won't be uploaded", EMsgType.FAIL); logPrinter.print("GL File provided have incorrect structure and won't be uploaded", EMsgType.FAIL);
status = EFileStatus.INCORRECT_FILE_FAILED; status = EFileStatus.INCORRECT_FILE_FAILED;
return; return;
} }
printLog("GL File structure validated and it will be uploaded", EMsgType.PASS); logPrinter.print("GL File structure validated and it will be uploaded", EMsgType.PASS);
if (initGoldLeafProtocol(pfsElement)) if (initGoldLeafProtocol(pfsElement))
status = EFileStatus.UPLOADED; // else - no change status that is already set to FAILED status = EFileStatus.UPLOADED; // else - no change status that is already set to FAILED
@ -560,9 +493,9 @@ public class UsbCommunications extends Task<Void> {
// Go connect to GoldLeaf // Go connect to GoldLeaf
if (writeToUsb(CMD_ConnectionRequest)) if (writeToUsb(CMD_ConnectionRequest))
printLog("GL Initiating GoldLeaf connection", EMsgType.PASS); logPrinter.print("GL Initiating GoldLeaf connection", EMsgType.PASS);
else { else {
printLog("GL Initiating GoldLeaf connection", EMsgType.FAIL); logPrinter.print("GL Initiating GoldLeaf connection", EMsgType.FAIL);
return false; return false;
} }
while (true) { while (true) {
@ -595,7 +528,7 @@ public class UsbCommunications extends Task<Void> {
continue; continue;
} }
if (Arrays.equals(readByte, CMD_Finish)) { if (Arrays.equals(readByte, CMD_Finish)) {
printLog("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS); logPrinter.print("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS);
break; break;
} }
} }
@ -605,24 +538,24 @@ public class UsbCommunications extends Task<Void> {
* ConnectionResponse command handler * ConnectionResponse command handler
* */ * */
private boolean handleConnectionResponse(PFSProvider pfsElement){ private boolean handleConnectionResponse(PFSProvider pfsElement){
printLog("GL 'ConnectionResonse' command:", EMsgType.INFO); logPrinter.print("GL 'ConnectionResponse' command:", EMsgType.INFO);
if (!writeToUsb(CMD_NSPName)) { if (!writeToUsb(CMD_NSPName)) {
printLog(" [1/3]", EMsgType.FAIL); logPrinter.print(" [1/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [1/3]", EMsgType.PASS); logPrinter.print(" [1/3]", EMsgType.PASS);
if (!writeToUsb(pfsElement.getBytesNspFileNameLength())) { if (!writeToUsb(pfsElement.getBytesNspFileNameLength())) {
printLog(" [2/3]", EMsgType.FAIL); logPrinter.print(" [2/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [2/3]", EMsgType.PASS); logPrinter.print(" [2/3]", EMsgType.PASS);
if (!writeToUsb(pfsElement.getBytesNspFileName())) { if (!writeToUsb(pfsElement.getBytesNspFileName())) {
printLog(" [3/3]", EMsgType.FAIL); logPrinter.print(" [3/3]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [3/3]", EMsgType.PASS); logPrinter.print(" [3/3]", EMsgType.PASS);
return true; return true;
} }
@ -630,43 +563,43 @@ public class UsbCommunications extends Task<Void> {
* Start command handler * Start command handler
* */ * */
private boolean handleStart(PFSProvider pfsElement){ private boolean handleStart(PFSProvider pfsElement){
printLog("GL Handle 'Start' command:", EMsgType.INFO); logPrinter.print("GL Handle 'Start' command:", EMsgType.INFO);
if (!writeToUsb(CMD_NSPData)) { if (!writeToUsb(CMD_NSPData)) {
printLog(" [Send command]", EMsgType.FAIL); logPrinter.print(" [Send command]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [Send command]", EMsgType.PASS); logPrinter.print(" [Send command]", EMsgType.PASS);
if (!writeToUsb(pfsElement.getBytesCountOfNca())) { if (!writeToUsb(pfsElement.getBytesCountOfNca())) {
printLog(" [Send length]", EMsgType.FAIL); logPrinter.print(" [Send length]", EMsgType.FAIL);
return false; return false;
} }
printLog(" [Send length]", EMsgType.PASS); logPrinter.print(" [Send length]", EMsgType.PASS);
int ncaCount = pfsElement.getIntCountOfNca(); int ncaCount = pfsElement.getIntCountOfNca();
printLog(" [Send information for "+ncaCount+" files]", EMsgType.INFO); logPrinter.print(" [Send information for "+ncaCount+" files]", EMsgType.INFO);
for (int i = 0; i < ncaCount; i++){ for (int i = 0; i < ncaCount; i++){
if (!writeToUsb(pfsElement.getNca(i).getNcaFileNameLength())) { if (!writeToUsb(pfsElement.getNca(i).getNcaFileNameLength())) {
printLog(" [1/4] File #"+i, EMsgType.FAIL); logPrinter.print(" [1/4] File #"+i, EMsgType.FAIL);
return false; return false;
} }
printLog(" [1/4] File #"+i, EMsgType.PASS); logPrinter.print(" [1/4] File #"+i, EMsgType.PASS);
if (!writeToUsb(pfsElement.getNca(i).getNcaFileName())) { if (!writeToUsb(pfsElement.getNca(i).getNcaFileName())) {
printLog(" [2/4] File #"+i, EMsgType.FAIL); logPrinter.print(" [2/4] File #"+i, EMsgType.FAIL);
return false; return false;
} }
printLog(" [2/4] File #"+i, EMsgType.PASS); logPrinter.print(" [2/4] File #"+i, EMsgType.PASS);
if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real. if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real.
printLog(" [2/4] File #"+i, EMsgType.FAIL); logPrinter.print(" [2/4] File #"+i, EMsgType.FAIL);
return false; return false;
} }
printLog(" [3/4] File #"+i, EMsgType.PASS); logPrinter.print(" [3/4] File #"+i, EMsgType.PASS);
if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size if (!writeToUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size
printLog(" [4/4] File #"+i, EMsgType.FAIL); logPrinter.print(" [4/4] File #"+i, EMsgType.FAIL);
return false; return false;
} }
printLog(" [4/4] File #"+i, EMsgType.PASS); logPrinter.print(" [4/4] File #"+i, EMsgType.PASS);
} }
return true; return true;
} }
@ -679,18 +612,18 @@ public class UsbCommunications extends Task<Void> {
int requestedNcaID; int requestedNcaID;
boolean isProgessBarInitiated = false; boolean isProgessBarInitiated = false;
if (isItRawRequest) { if (isItRawRequest) {
printLog("GL Handle 'Content' command", EMsgType.INFO); logPrinter.print("GL Handle 'Content' command", EMsgType.INFO);
byte[] readByte = readFromUsb(); byte[] readByte = readFromUsb();
if (readByte == null || readByte.length != 4) { if (readByte == null || readByte.length != 4) {
printLog(" [Read requested ID]", EMsgType.FAIL); logPrinter.print(" [Read requested ID]", EMsgType.FAIL);
return false; return false;
} }
requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt(); requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt();
printLog(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS); logPrinter.print(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS);
} }
else { else {
requestedNcaID = pfsElement.getNcaTicketID(); requestedNcaID = pfsElement.getNcaTicketID();
printLog("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO); logPrinter.print("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO);
} }
long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize(); long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize();
@ -723,11 +656,11 @@ public class UsbCommunications extends Task<Void> {
if (isProgessBarInitiated){ if (isProgessBarInitiated){
try { try {
if (readFrom+readPice == realNcaSize){ if (readFrom+readPice == realNcaSize){
progressQueue.put(1.0); logPrinter.updateProgress(1.0);
isProgessBarInitiated = false; isProgessBarInitiated = false;
} }
else else
progressQueue.put((readFrom+readPice)/(realNcaSize/100.0) / 100.0); logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
}catch (InterruptedException ie){ }catch (InterruptedException ie){
getException().printStackTrace(); // TODO: Do something with this getException().printStackTrace(); // TODO: Do something with this
} }
@ -742,7 +675,7 @@ public class UsbCommunications extends Task<Void> {
bufferedInStream.close(); bufferedInStream.close();
} }
catch (IOException ioe){ catch (IOException ioe){
printLog(" Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL); logPrinter.print(" Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL);
ioe.printStackTrace(); ioe.printStackTrace();
return false; return false;
} }
@ -754,30 +687,27 @@ public class UsbCommunications extends Task<Void> {
* Correct exit * Correct exit
* */ * */
private void close(){ private void close(){
// close handler in the end // Close handler in the end
if (handlerNS != null) { if (handlerNS != null) {
// Try to release interface // Try to release interface
int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE); int result = LibUsb.releaseInterface(handlerNS, DEFAULT_INTERFACE);
if (result != LibUsb.SUCCESS) if (result != LibUsb.SUCCESS)
printLog("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING); logPrinter.print("Release interface\n Returned: "+result+" (sometimes it's not an issue)", EMsgType.WARNING);
else else
printLog("Release interface", EMsgType.PASS); logPrinter.print("Release interface", EMsgType.PASS);
LibUsb.close(handlerNS); LibUsb.close(handlerNS);
printLog("Requested handler close", EMsgType.INFO); logPrinter.print("Requested handler updateAndClose", EMsgType.INFO);
} }
// close context in the end // Close context in the end
if (contextNS != null) { if (contextNS != null) {
LibUsb.exit(contextNS); LibUsb.exit(contextNS);
printLog("Requested context close", EMsgType.INFO); logPrinter.print("Requested context updateAndClose", EMsgType.INFO);
} }
// Report status // Report status and close
for (String fileName: nspMap.keySet()) logPrinter.updateAndClose(nspMap, status);
statusMap.put(fileName, status);
msgConsumer.interrupt();
} }
/** /**
* Sending any byte array to USB device * Sending any byte array to USB device
@ -792,27 +722,12 @@ public class UsbCommunications extends Task<Void> {
int result; int result;
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01 result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01
if (result != LibUsb.SUCCESS){ if (result != LibUsb.SUCCESS){
switch (result){ logPrinter.print("Data transfer (write) issue\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
case LibUsb.ERROR_TIMEOUT: logPrinter.print("Execution stopped", EMsgType.FAIL);
printLog("Data transfer (write) issue\n Returned: ERROR_TIMEOUT", EMsgType.FAIL);
break;
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
printLog("Data transfer (write) issue\n Returned: ERROR_PIPE", EMsgType.FAIL);
break;
case LibUsb.ERROR_OVERFLOW:
printLog("Data transfer (write) issue\n Returned: ERROR_OVERFLOW", EMsgType.FAIL);
break;
case LibUsb.ERROR_NO_DEVICE:
printLog("Data transfer (write) issue\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
default:
printLog("Data transfer (write) issue\n Returned: "+result, EMsgType.FAIL);
}
printLog("Execution stopped", EMsgType.FAIL);
return false; return false;
}else { }else {
if (writeBufTransferred.get() != message.length){ if (writeBufTransferred.get() != message.length){
printLog("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL); logPrinter.print("Data transfer (write) issue\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL);
return false; return false;
} }
else { else {
@ -834,26 +749,8 @@ public class UsbCommunications extends Task<Void> {
result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 0); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81
if (result != LibUsb.SUCCESS){ if (result != LibUsb.SUCCESS){
switch (result){ logPrinter.print("Data transfer (read) issue\n Returned: "+UsbErrorCodes.getErrCode(result), EMsgType.FAIL);
case LibUsb.ERROR_TIMEOUT: logPrinter.print("Execution stopped", EMsgType.FAIL);
printLog("Data transfer (read) issue\n Returned: ERROR_TIMEOUT", EMsgType.FAIL);
break;
case LibUsb.ERROR_PIPE: //WUT?? I dunno man looks overkill in here..
printLog("Data transfer (read) issue\n Returned: ERROR_PIPE", EMsgType.FAIL);
break;
case LibUsb.ERROR_OVERFLOW:
printLog("Data transfer (read) issue\n Returned: ERROR_OVERFLOW", EMsgType.FAIL);
break;
case LibUsb.ERROR_NO_DEVICE:
printLog("Data transfer (read) issue\n Returned: ERROR_NO_DEVICE", EMsgType.FAIL);
break;
case LibUsb.ERROR_IO:
printLog("Data transfer (read) issue\n Returned: ERROR_IO", EMsgType.FAIL);
break;
default:
printLog("Data transfer (read) issue\n Returned: "+result, EMsgType.FAIL);
}
printLog("Execution stopped", EMsgType.FAIL);
return null; return null;
} else { } else {
int trans = readBufTransferred.get(); int trans = readBufTransferred.get();
@ -865,30 +762,4 @@ public class UsbCommunications extends Task<Void> {
return receivedBytes; return receivedBytes;
} }
} }
/**
* This is what will print to textArea of the application.
* */
private void printLog(String message, EMsgType type){
try {
switch (type){
case PASS:
msgQueue.put("[ PASS ] "+message+"\n");
break;
case FAIL:
msgQueue.put("[ FAIL ] "+message+"\n");
break;
case INFO:
msgQueue.put("[ INFO ] "+message+"\n");
break;
case WARNING:
msgQueue.put("[ WARN ] "+message+"\n");
break;
default:
msgQueue.put(message);
}
}catch (InterruptedException ie){
ie.printStackTrace();
}
}
} }

View file

@ -0,0 +1,38 @@
package nsusbloader.USB;
import org.usb4java.LibUsb;
class UsbErrorCodes {
static String getErrCode(int value){
switch (value){
case LibUsb.ERROR_ACCESS:
return "ERROR_ACCESS";
case LibUsb.ERROR_BUSY:
return "ERROR_BUSY";
case LibUsb.ERROR_INTERRUPTED:
return "ERROR_INTERRUPTED";
case LibUsb.ERROR_INVALID_PARAM:
return "ERROR_INVALID_PARAM";
case LibUsb.ERROR_IO:
return "ERROR_IO";
case LibUsb.ERROR_NO_DEVICE:
return "ERROR_NO_DEVICE";
case LibUsb.ERROR_NO_MEM:
return "ERROR_NO_MEM";
case LibUsb.ERROR_NOT_FOUND:
return "ERROR_NOT_FOUND";
case LibUsb.ERROR_NOT_SUPPORTED:
return "ERROR_NOT_SUPPORTED";
case LibUsb.ERROR_OTHER:
return "ERROR_OTHER";
case LibUsb.ERROR_OVERFLOW:
return "ERROR_OVERFLOW";
case LibUsb.ERROR_PIPE:
return "ERROR_PIPE";
case LibUsb.ERROR_TIMEOUT:
return "ERROR_TIMEOUT";
default:
return Integer.toString(value);
}
}
}

View file

@ -5,7 +5,7 @@ logsEnteredAsMsg2=\u0427\u0442\u043E\u0431\u044B \u0438\u0437\u0431\u0435\u0436\
logsFilesToUploadTitle=\u0424\u0430\u0439\u043B\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438: logsFilesToUploadTitle=\u0424\u0430\u0439\u043B\u044B \u0434\u043B\u044F \u0437\u0430\u0433\u0440\u0443\u0437\u043A\u0438:
logsGreetingsMessage=\u0414\u043E\u0431\u0440\u043E \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C \u0432 NS-USBloader logsGreetingsMessage=\u0414\u043E\u0431\u0440\u043E \u043F\u043E\u0436\u0430\u043B\u043E\u0432\u0430\u0442\u044C \u0432 NS-USBloader
logsNoFolderFileSelected=\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B - \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E. logsNoFolderFileSelected=\u0424\u0430\u0439\u043B\u044B \u043D\u0435 \u0432\u044B\u0431\u0440\u0430\u043D\u044B - \u0437\u0430\u0433\u0440\u0443\u0436\u0430\u0442\u044C \u043D\u0435\u0447\u0435\u0433\u043E.
windowBodyConfirmExit=\u0421\u0435\u0439\u0447\u0430\u0441 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u0440\u0435\u0440\u0432\u0451\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0443.\n\u042D\u0442\u043E \u0445\u0443\u0434\u0448\u0435\u0435 \u0447\u0442\u043E \u0442\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0441\u0435\u0439\u0447\u0430\u0441 \u0441\u0434\u0435\u043B\u0430\u0442\u044C.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438 \u0432\u044B\u0439\u0442\u0438? windowBodyConfirmExit=\u0421\u0435\u0439\u0447\u0430\u0441 \u043F\u0440\u043E\u0438\u0441\u0445\u043E\u0434\u0438\u0442 \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u043D\u044B\u0445 \u0438 \u0437\u0430\u043A\u0440\u044B\u0442\u0438\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F \u043F\u0440\u0435\u0440\u0432\u0451\u0442 \u0435\u0451.\n\u042D\u0442\u043E \u0445\u0443\u0434\u0448\u0435\u0435 \u0447\u0442\u043E \u0442\u044B \u043C\u043E\u0436\u0435\u0448\u044C \u0441\u0435\u0439\u0447\u0430\u0441 \u0441\u0434\u0435\u043B\u0430\u0442\u044C.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C \u043F\u0440\u043E\u0446\u0435\u0441\u0441 \u0438 \u0432\u044B\u0439\u0442\u0438?
windowTitleConfirmExit=\u041D\u0435\u0442, \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C! windowTitleConfirmExit=\u041D\u0435\u0442, \u043E\u0441\u0442\u0430\u043D\u043E\u0432\u0438\u0441\u044C!
btnStop=\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C btnStop=\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u044C
logsGreetingsMessage2=--\n\ logsGreetingsMessage2=--\n\

View file

@ -0,0 +1,21 @@
btnFileOpen=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 .NSP \u0444\u0430\u0439\u043B\u0438
btnUpload=\u0412\u0456\u0434\u0456\u0441\u043B\u0430\u0442\u0438 \u0443 NS
logsEnteredAsMsg1=\u0412\u0438 \u0443\u0432\u0456\u0439\u0448\u043B\u0438 \u044F\u043A:
logsEnteredAsMsg2=\u0414\u043B\u044F \u0437\u0430\u043F\u043E\u0431\u0456\u0433\u0430\u043D\u043D\u044F \u043F\u043E\u043C\u0438\u043B\u043E\u043A \u0432\u0438 \u043C\u0430\u0454\u0442\u0435 \u0431\u0443\u0442\u0438 root \u0430\u0431\u043E \u043D\u0430\u0441\u0442\u0440\u043E\u0457\u0442\u0438 \u043F\u0440\u0430\u0432\u0438\u043B\u0430 'udev' \u0434\u043B\u044F \u0446\u044C\u043E\u0433\u043E \u043A\u043E\u0440\u0438\u0441\u0442\u0443\u0432\u0430\u0447\u0430.
logsFilesToUploadTitle=\u0424\u0430\u0439\u043B\u0438 \u0434\u043B\u044F \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0435\u043D\u043D\u044F:
logsGreetingsMessage=\u041B\u0430\u0441\u043A\u0430\u0432\u043E \u043F\u0440\u043E\u0441\u0438\u043C\u043E \u0434\u043E NS-USBloader
logsNoFolderFileSelected=\u0424\u0430\u0439\u043B\u0438 \u043D\u0435 \u0432\u0438\u0431\u0440\u0430\u043D\u0456 - \u0437\u0430\u0432\u0430\u043D\u0442\u0430\u0436\u0443\u0432\u0430\u0442\u0438 \u043D\u0456\u0447\u043E\u0433\u043E.
windowBodyConfirmExit=\u0417\u0430\u0440\u0430\u0437 \u0432\u0456\u0434\u0431\u0443\u0432\u0430\u0454\u0442\u044C\u0441\u044F \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0430 \u0434\u0430\u043D\u0438\u0445 \u0456 \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043D\u043D\u044F \u0434\u043E\u0434\u0430\u0442\u043A\u0443 \u043F\u0440\u0435\u0440\u0432\u0435 \u0457\u0457.\n\u0426\u0435 \u043D\u0430\u0439\u0433\u0456\u0440\u0448\u0435 \u0449\u043E \u0442\u0438 \u043C\u043E\u0436\u0435\u0448 \u0437\u0430\u0440\u0430\u0437 \u0437\u0440\u043E\u0431\u0438\u0442\u0438.\n\u041F\u0440\u0435\u0440\u0432\u0430\u0442\u0438 \u043F\u0440\u043E\u0446\u0435\u0441 \u0456 \u0432\u0438\u0439\u0442\u0438?
windowTitleConfirmExit=\u041D\u0456, \u0437\u0443\u043F\u0438\u043D\u0438\u0441\u044C!
btnStop=\u041F\u0435\u0440\u0435\u0440\u0432\u0430\u0442\u0438
logsGreetingsMessage2=--\n\
\u0421\u0438\u0440\u0446\u0435\u0432\u0438\u0439 \u043A\u043E\u0434: https://github.com/developersu/ns-usbloader/\n\
\u0421\u0430\u0439\u0442: https://developersu.blogspot.com/search/label/NS-USBloader\n\
\u0418\u0441\u0430\u0454\u043D\u043A\u043E \u0414\u043C\u0438\u0442\u0440\u043E [developer.su]
windowTitleConfirmWrongPFS0=\u041D\u0435\u0432\u0456\u0440\u043D\u0438\u0439 \u0442\u0438\u043F \u0444\u0430\u0439\u043B\u0443
windowBodyConfirmWrongPFS0=\u0412\u0438\u0431\u0440\u0430\u043D\u0438\u0439 \u0444\u0430\u0439\u043B NSP \u043C\u0430\u0454 \u043D\u0435\u0432\u0456\u0440\u043D\u0456 \u0441\u0438\u043C\u0432\u043E\u043B\u0438. \u0421\u043A\u043E\u0440\u0456\u0448\u0435 \u0437\u0430 \u0432\u0441\u0435 \u0432\u0456\u043D \u043F\u043E\u0448\u043A\u043E\u0434\u0436\u0435\u043D\u0438\u0439.\n\
\u041A\u0440\u0430\u0449\u0435 \u0437\u0443\u043F\u0438\u043D\u0438\u0442\u0438\u0441\u044F \u043F\u0440\u044F\u043C\u043E \u0437\u0430\u0440\u0430\u0437. \u0425\u043E\u0447\u0435\u0448 \u043F\u0440\u043E\u0434\u043E\u0432\u0436\u0438\u0442\u0438 \u043D\u0435 \u0437\u0432\u0430\u0436\u0430\u044E\u0447\u0438 \u043D\u0456 \u043D\u0430 \u0449\u043E?
tableStatusLbl=\u0421\u0442\u0430\u043D
tableFileNameLbl=\u0406\u043C'\u044F \u0444\u0430\u0439\u043B\u0443
tableSizeLbl=\u0420\u043E\u0437\u043C\u0456\u0440 (~\u041C\u0431)
tableUploadLbl=\u0417\u0430\u0432\u0430\u043D\u0442\u0430\u0436.?