Add split-files support for every 'protocol' supported
This commit is contained in:
parent
077aa9b0d8
commit
049c07fe8d
28 changed files with 618 additions and 265 deletions
|
@ -12,7 +12,7 @@ With GUI and cookies. Works on Windows, macOS and Linux.
|
||||||
|
|
||||||
Sometimes I add new posts about this project [on my home page](https://developersu.blogspot.com/search/label/NS-USBloader).
|
Sometimes I add new posts about this project [on my home page](https://developersu.blogspot.com/search/label/NS-USBloader).
|
||||||
|
|
||||||
![Screenshot](https://farm8.staticflickr.com/7809/46703921964_53f60f04ed_o.png)
|
![Screenshot](https://live.staticflickr.com/65535/48962978677_4c3913e8a9_o.png)
|
||||||
|
|
||||||
#### License
|
#### License
|
||||||
|
|
||||||
|
@ -99,7 +99,7 @@ There are three tabs. First one is main.
|
||||||
|
|
||||||
At the top of you selecting from drop-down application and protocol that you're going to use. For GoldLeaf only USB is available. Lamp icon stands for switching themes (light or dark).
|
At the top of you selecting from drop-down application and protocol that you're going to use. For GoldLeaf only USB is available. Lamp icon stands for switching themes (light or dark).
|
||||||
|
|
||||||
Then you may drag-n-drop folder with NSPs OR files to application or use 'Select NSP files' button. Multiple selection for files available. Click it again and select files from another folder it you want, it will be added into the table.
|
Then you may drag-n-drop files (split-files aka folders) to application or use 'Select NSP files' button. Multiple selection for files available. Click it again and select files from another folder it you want, it will be added into the table.
|
||||||
|
|
||||||
Table.
|
Table.
|
||||||
|
|
||||||
|
@ -140,13 +140,16 @@ usb4java since NS-USBloader-v0.2.3 switched to 1.2.0 instead of 1.3.0. This shou
|
||||||
|
|
||||||
### Translators!
|
### Translators!
|
||||||
If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it.
|
If you want to see this app translated to your language, go grab [this file](https://github.com/developersu/ns-usbloader/blob/master/src/main/resources/locale.properties) and translate it.
|
||||||
|
|
||||||
Upload somewhere (create PR, use pastebin/google drive/whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add.
|
Upload somewhere (create PR, use pastebin/google drive/whatever else). [Create new issue](https://github.com/developersu/ns-usbloader/issues) and post a link. I'll grab it and add.
|
||||||
|
|
||||||
|
To convert files of any locale to readable format (and vise-versa) you can use this site [https://itpro.cz/juniconv/](https://itpro.cz/juniconv/)
|
||||||
|
|
||||||
|
|
||||||
#### TODO (maybe):
|
#### TODO (maybe):
|
||||||
- [x] [Android support](https://github.com/developersu/ns-usbloader-mobile)
|
- [x] [Android support](https://github.com/developersu/ns-usbloader-mobile)
|
||||||
- [ ] File order sort (non-critical)
|
- [ ] File order sort (non-critical)
|
||||||
- [ ] More deep file analyze before uploading.
|
- [ ] More deep file analyze before uploading.
|
||||||
- [ ] XCI support
|
|
||||||
|
|
||||||
## Support this app
|
## Support this app
|
||||||
|
|
||||||
|
|
2
pom.xml
2
pom.xml
|
@ -8,7 +8,7 @@
|
||||||
<name>NS-USBloader</name>
|
<name>NS-USBloader</name>
|
||||||
|
|
||||||
<artifactId>ns-usbloader</artifactId>
|
<artifactId>ns-usbloader</artifactId>
|
||||||
<version>0.8.2-SNAPSHOT</version>
|
<version>0.9-SNAPSHOT</version>
|
||||||
|
|
||||||
<url>https://github.com/developersu/ns-usbloader/</url>
|
<url>https://github.com/developersu/ns-usbloader/</url>
|
||||||
<description>
|
<description>
|
||||||
|
|
91
src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java
Normal file
91
src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
package nsusbloader.COM.Helpers;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Split files
|
||||||
|
* */
|
||||||
|
public class NSSplitReader implements Closeable {
|
||||||
|
|
||||||
|
private final String splitFileDir;
|
||||||
|
private final long referenceSplitChunkSize;
|
||||||
|
|
||||||
|
private byte subFileNum;
|
||||||
|
private long curPosition;
|
||||||
|
private BufferedInputStream biStream;
|
||||||
|
|
||||||
|
public NSSplitReader(File file, long seekToPosition) throws IOException, NullPointerException {
|
||||||
|
this.splitFileDir = file.getAbsolutePath()+File.separator;
|
||||||
|
File subFile = new File(file.getAbsolutePath()+File.separator+"00");
|
||||||
|
if (! file.exists())
|
||||||
|
throw new FileNotFoundException("File not found on "+file.getAbsolutePath()+File.separator+"00");
|
||||||
|
this.referenceSplitChunkSize = subFile.length();
|
||||||
|
this.subFileNum = (byte) (seekToPosition / referenceSplitChunkSize);
|
||||||
|
this.biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));
|
||||||
|
this.curPosition = seekToPosition;
|
||||||
|
|
||||||
|
seekToPosition -= referenceSplitChunkSize * subFileNum;
|
||||||
|
|
||||||
|
if (seekToPosition != biStream.skip(seekToPosition))
|
||||||
|
throw new IOException("Unable to seek to requested position of "+seekToPosition+" for file "+splitFileDir+String.format("%02d", subFileNum));
|
||||||
|
}
|
||||||
|
|
||||||
|
public long seek(long position) throws IOException{
|
||||||
|
|
||||||
|
byte subFileRequested = (byte) (position / referenceSplitChunkSize);
|
||||||
|
|
||||||
|
if ((subFileRequested != this.subFileNum) || (curPosition > position)) {
|
||||||
|
biStream.close();
|
||||||
|
biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileRequested)));
|
||||||
|
this.subFileNum = subFileRequested;
|
||||||
|
this.curPosition = referenceSplitChunkSize * subFileRequested;
|
||||||
|
}
|
||||||
|
|
||||||
|
long retVal = biStream.skip(position - curPosition);
|
||||||
|
|
||||||
|
retVal += curPosition;
|
||||||
|
this.curPosition = position;
|
||||||
|
return retVal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int read(byte[] readBuffer) throws IOException, NullPointerException {
|
||||||
|
final int requested = readBuffer.length;
|
||||||
|
int readPrtOne;
|
||||||
|
|
||||||
|
if ( (curPosition + requested) <= (referenceSplitChunkSize * (subFileNum+1))) {
|
||||||
|
if ((readPrtOne = biStream.read(readBuffer)) < 0 )
|
||||||
|
return readPrtOne;
|
||||||
|
curPosition += readPrtOne;
|
||||||
|
return readPrtOne;
|
||||||
|
}
|
||||||
|
|
||||||
|
int partOne = (int) (referenceSplitChunkSize * (subFileNum+1) - curPosition);
|
||||||
|
int partTwo = requested - partOne;
|
||||||
|
int readPrtTwo;
|
||||||
|
|
||||||
|
if ( (readPrtOne = biStream.read(readBuffer, 0, partOne)) < 0)
|
||||||
|
return readPrtOne;
|
||||||
|
|
||||||
|
curPosition += readPrtOne;
|
||||||
|
|
||||||
|
if (readPrtOne != partOne)
|
||||||
|
return readPrtOne;
|
||||||
|
|
||||||
|
biStream.close();
|
||||||
|
subFileNum += 1;
|
||||||
|
biStream = new BufferedInputStream(new FileInputStream(splitFileDir + String.format("%02d", subFileNum)));
|
||||||
|
|
||||||
|
if ( (readPrtTwo = biStream.read(readBuffer, partOne, partTwo) ) < 0)
|
||||||
|
return readPrtTwo;
|
||||||
|
|
||||||
|
curPosition += readPrtTwo;
|
||||||
|
|
||||||
|
return readPrtOne + readPrtTwo;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() throws IOException {
|
||||||
|
if (biStream != null)
|
||||||
|
biStream.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,9 +1,10 @@
|
||||||
package nsusbloader.NET;
|
package nsusbloader.COM.NET;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import nsusbloader.COM.Helpers.NSSplitReader;
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
|
@ -22,6 +23,7 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
private String switchIP;
|
private String switchIP;
|
||||||
|
|
||||||
private HashMap<String, File> nspMap;
|
private HashMap<String, File> nspMap;
|
||||||
|
private HashMap<String, Long> nspFileSizes;
|
||||||
|
|
||||||
private ServerSocket serverSocket;
|
private ServerSocket serverSocket;
|
||||||
|
|
||||||
|
@ -42,6 +44,29 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
this.switchIP = switchIP;
|
this.switchIP = switchIP;
|
||||||
this.logPrinter = new LogPrinter();
|
this.logPrinter = new LogPrinter();
|
||||||
this.nspMap = new HashMap<>();
|
this.nspMap = new HashMap<>();
|
||||||
|
this.nspFileSizes = new HashMap<>();
|
||||||
|
// Filter and remove empty/incorrect split-files
|
||||||
|
filesList.removeIf(f -> {
|
||||||
|
if (f.isDirectory()){
|
||||||
|
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||||
|
if (subFiles == null || subFiles.length == 0) {
|
||||||
|
logPrinter.print("NET: Removing empty folder: " + f.getName(), EMsgType.WARNING);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
|
||||||
|
|
||||||
|
for (int i = subFiles.length - 2; i > 0 ; i--){
|
||||||
|
if (subFiles[i].length() < subFiles[i-1].length()) {
|
||||||
|
logPrinter.print("NET: Removing strange split file: "+f.getName()+
|
||||||
|
"\n (Chunk sizes of the split file are not the same, but has to be.)", EMsgType.WARNING);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
});
|
||||||
// Collect and encode NSP files list
|
// Collect and encode NSP files list
|
||||||
try {
|
try {
|
||||||
for (File nspFile : filesList)
|
for (File nspFile : filesList)
|
||||||
|
@ -49,12 +74,27 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
}
|
}
|
||||||
catch (UnsupportedEncodingException uee){
|
catch (UnsupportedEncodingException uee){
|
||||||
isValid = false;
|
isValid = false;
|
||||||
logPrinter.print("NET: Unsupported encoding for 'URLEncoder'. Internal issue you can't fix. Please report. Returned:\n\t"+uee.getMessage(), EMsgType.FAIL);
|
logPrinter.print("NET: Unsupported encoding for 'URLEncoder'. Internal issue you can't fix. Please report. Returned:\n\t"+uee.getMessage(), EMsgType.FAIL);
|
||||||
for (File nspFile : filesList)
|
//for (File nspFile : filesList)
|
||||||
nspMap.put(nspFile.getName(), nspFile);
|
// nspMap.put(nspFile.getName(), nspFile);
|
||||||
close(EFileStatus.FAILED);
|
close(EFileStatus.FAILED);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// Collect sizes since now we can have split-files support
|
||||||
|
for (Map.Entry<String, File> entry : nspMap.entrySet()){
|
||||||
|
File inFile = entry.getValue();
|
||||||
|
long fSize = 0;
|
||||||
|
if (inFile.isDirectory()){
|
||||||
|
File[] subFiles = inFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||||
|
for (File subFile : subFiles)
|
||||||
|
fSize += subFile.length();
|
||||||
|
nspFileSizes.put(entry.getKey(), fSize);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nspFileSizes.put(entry.getKey(), inFile.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
// Resolve IP
|
// Resolve IP
|
||||||
if (hostIPaddr.isEmpty()) {
|
if (hostIPaddr.isEmpty()) {
|
||||||
DatagramSocket socket;
|
DatagramSocket socket;
|
||||||
|
@ -242,23 +282,23 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
LinkedList<String> tcpPacket = new LinkedList<>();
|
LinkedList<String> tcpPacket = new LinkedList<>();
|
||||||
|
|
||||||
while ((line = br.readLine()) != null) {
|
while ((line = br.readLine()) != null) {
|
||||||
//System.out.println(line); // TODO: remove DBG
|
//System.out.println(line); // Debug
|
||||||
if (line.trim().isEmpty()) { // If TCP packet is ended
|
if (line.trim().isEmpty()) { // If TCP packet is ended
|
||||||
if (handleRequest(tcpPacket)) // Proceed required things
|
if (handleRequest(tcpPacket)) // Proceed required things
|
||||||
break work_routine;
|
break work_routine;
|
||||||
tcpPacket.clear(); // Clear data and wait for next TCP packet
|
tcpPacket.clear(); // Clear data and wait for next TCP packet
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
tcpPacket.add(line); // Otherwise collect data
|
tcpPacket.add(line); // Otherwise collect data
|
||||||
}
|
}
|
||||||
// and reopen client sock
|
// and reopen client sock
|
||||||
clientSocket.close();
|
clientSocket.close();
|
||||||
}
|
}
|
||||||
catch (IOException ioe){ // If server socket closed, then client socket also closed.
|
catch (IOException ioe){ // If server socket closed, then client socket also closed.
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!isCancelled())
|
if ( ! isCancelled() )
|
||||||
close(EFileStatus.UNKNOWN);
|
close(EFileStatus.UNKNOWN);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -271,8 +311,11 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
private boolean handleRequest(LinkedList<String> packet){
|
private boolean handleRequest(LinkedList<String> packet){
|
||||||
//private boolean handleRequest(LinkedList<String> packet, OutputStreamWriter pw){
|
//private boolean handleRequest(LinkedList<String> packet, OutputStreamWriter pw){
|
||||||
File requestedFile;
|
File requestedFile;
|
||||||
requestedFile = nspMap.get(packet.get(0).replaceAll("(^[A-z\\s]+/)|(\\s+?.*$)", ""));
|
String reqFileName = packet.get(0).replaceAll("(^[A-z\\s]+/)|(\\s+?.*$)", "");
|
||||||
if (!requestedFile.exists() || requestedFile.length() == 0){ // well.. tell 404 if file exists with 0 length is against standard, but saves time
|
long reqFileSize = nspFileSizes.get(reqFileName);
|
||||||
|
requestedFile = nspMap.get(reqFileName);
|
||||||
|
|
||||||
|
if (!requestedFile.exists() || reqFileSize == 0){ // well.. tell 404 if file exists with 0 length is against standard, but saves time
|
||||||
currSockPW.write(NETPacket.getCode404());
|
currSockPW.write(NETPacket.getCode404());
|
||||||
currSockPW.flush();
|
currSockPW.flush();
|
||||||
logPrinter.print("NET: File "+requestedFile.getName()+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
|
logPrinter.print("NET: File "+requestedFile.getName()+" doesn't exists or have 0 size. Returning 404", EMsgType.FAIL);
|
||||||
|
@ -280,7 +323,7 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (packet.get(0).startsWith("HEAD")){
|
if (packet.get(0).startsWith("HEAD")){
|
||||||
currSockPW.write(NETPacket.getCode200(requestedFile.length()));
|
currSockPW.write(NETPacket.getCode200(reqFileSize));
|
||||||
currSockPW.flush();
|
currSockPW.flush();
|
||||||
logPrinter.print("NET: Replying for requested file: "+requestedFile.getName(), EMsgType.INFO);
|
logPrinter.print("NET: Replying for requested file: "+requestedFile.getName(), EMsgType.INFO);
|
||||||
return false;
|
return false;
|
||||||
|
@ -298,23 +341,23 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
logPrinter.update(requestedFile, EFileStatus.FAILED);
|
logPrinter.update(requestedFile, EFileStatus.FAILED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (writeToSocket(requestedFile, Long.parseLong(rangeStr[0]), Long.parseLong(rangeStr[1]))) // DO WRITE
|
if (writeToSocket(reqFileName, Long.parseLong(rangeStr[0]), Long.parseLong(rangeStr[1]))) // DO WRITE
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (!rangeStr[0].isEmpty()) { // If only START defined: Read all
|
else if (!rangeStr[0].isEmpty()) { // If only START defined: Read all
|
||||||
if (writeToSocket(requestedFile, Long.parseLong(rangeStr[0]), requestedFile.length())) // DO WRITE
|
if (writeToSocket(reqFileName, Long.parseLong(rangeStr[0]), reqFileSize)) // DO WRITE
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else if (!rangeStr[1].isEmpty()) { // If only END defined: Try to read last 500 bytes
|
else if (!rangeStr[1].isEmpty()) { // If only END defined: Try to read last 500 bytes
|
||||||
if (requestedFile.length() > 500){
|
if (reqFileSize > 500){
|
||||||
if (writeToSocket(requestedFile, requestedFile.length()-500, requestedFile.length())) // DO WRITE
|
if (writeToSocket(reqFileName, reqFileSize-500, reqFileSize)) // DO WRITE
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
else { // If file smaller than 500 bytes
|
else { // If file smaller than 500 bytes
|
||||||
currSockPW.write(NETPacket.getCode416());
|
currSockPW.write(NETPacket.getCode416());
|
||||||
currSockPW.flush();
|
currSockPW.flush();
|
||||||
logPrinter.print("NET: File size requested for "+requestedFile.getName()+" while actual size of it: "+requestedFile.length()+". Returning 416", EMsgType.FAIL);
|
logPrinter.print("NET: File size requested for "+requestedFile.getName()+" while actual size of it: "+reqFileSize+". Returning 416", EMsgType.FAIL);
|
||||||
logPrinter.update(requestedFile, EFileStatus.FAILED);
|
logPrinter.update(requestedFile, EFileStatus.FAILED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -343,50 +386,84 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
/**
|
/**
|
||||||
* Send files.
|
* Send files.
|
||||||
* */
|
* */
|
||||||
private boolean writeToSocket(File file, long start, long end){
|
private boolean writeToSocket(String fileName, long start, long end){
|
||||||
|
File reqFile = nspMap.get(fileName);
|
||||||
|
// Inform
|
||||||
logPrinter.print("NET: Responding to requested range: "+start+"-"+end, EMsgType.INFO);
|
logPrinter.print("NET: Responding to requested range: "+start+"-"+end, EMsgType.INFO);
|
||||||
currSockPW.write(NETPacket.getCode206(file.length(), start, end));
|
// Reply
|
||||||
|
currSockPW.write(NETPacket.getCode206(nspFileSizes.get(fileName), start, end));
|
||||||
currSockPW.flush();
|
currSockPW.flush();
|
||||||
|
// Prepare transfer
|
||||||
|
long count = end - start + 1;
|
||||||
|
|
||||||
|
int readPice = 8388608; // = 8Mb
|
||||||
|
byte[] byteBuf;
|
||||||
|
long currentOffset = 0;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
long count = end - start + 1;
|
//================================= SPLIT FILE ====================================
|
||||||
|
if (reqFile.isDirectory()){
|
||||||
|
NSSplitReader nsr = new NSSplitReader(reqFile, start);
|
||||||
|
|
||||||
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file));
|
while (currentOffset < count){
|
||||||
int readPice = 8388608; // = 8Mb
|
if (isCancelled())
|
||||||
byte[] byteBuf;
|
return true;
|
||||||
|
if ((currentOffset + readPice) >= count){
|
||||||
|
readPice = Math.toIntExact(count - currentOffset);
|
||||||
|
}
|
||||||
|
byteBuf = new byte[readPice];
|
||||||
|
|
||||||
if (bis.skip(start) != start){
|
if (nsr.read(byteBuf) != readPice){
|
||||||
logPrinter.print("NET: Unable to skip requested range.", EMsgType.FAIL);
|
logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL);
|
||||||
logPrinter.update(file, EFileStatus.FAILED);
|
return true;
|
||||||
return true;
|
}
|
||||||
}
|
currSockOS.write(byteBuf);
|
||||||
long currentOffset = 0;
|
//-------/
|
||||||
while (currentOffset < count){
|
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
|
||||||
if (isCancelled())
|
//-------/
|
||||||
return true;
|
currentOffset += readPice;
|
||||||
if ((currentOffset+readPice) >= count){
|
|
||||||
readPice = Math.toIntExact(count - currentOffset);
|
|
||||||
}
|
}
|
||||||
byteBuf = new byte[readPice];
|
currSockOS.flush(); // TODO: check if this really needed.
|
||||||
|
nsr.close();
|
||||||
|
}
|
||||||
|
//================================= REGULAR FILE ====================================
|
||||||
|
else {
|
||||||
|
BufferedInputStream bis = new BufferedInputStream(new FileInputStream(reqFile));
|
||||||
|
|
||||||
if (bis.read(byteBuf) != readPice){
|
if (bis.skip(start) != start){
|
||||||
logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL);
|
logPrinter.print("NET: Unable to skip requested range.", EMsgType.FAIL);
|
||||||
|
logPrinter.update(reqFile, EFileStatus.FAILED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
currSockOS.write(byteBuf);
|
|
||||||
//-----------------------------------------/
|
while (currentOffset < count){
|
||||||
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
|
if (isCancelled())
|
||||||
//-----------------------------------------/
|
return true;
|
||||||
currentOffset += readPice;
|
if ((currentOffset + readPice) >= count){
|
||||||
|
readPice = Math.toIntExact(count - currentOffset);
|
||||||
|
}
|
||||||
|
byteBuf = new byte[readPice];
|
||||||
|
|
||||||
|
if (bis.read(byteBuf) != readPice){
|
||||||
|
logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
currSockOS.write(byteBuf);
|
||||||
|
//-------/
|
||||||
|
logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0);
|
||||||
|
//-------/
|
||||||
|
currentOffset += readPice;
|
||||||
|
}
|
||||||
|
currSockOS.flush(); // TODO: check if this really needed.
|
||||||
|
bis.close();
|
||||||
}
|
}
|
||||||
currSockOS.flush(); // TODO: check if this really needed.
|
//-------/
|
||||||
bis.close();
|
|
||||||
//-----------------------------------------/
|
|
||||||
logPrinter.updateProgress(1.0);
|
logPrinter.updateProgress(1.0);
|
||||||
//-----------------------------------------/
|
//-------/
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (IOException | NullPointerException e){
|
||||||
logPrinter.print("NET: File transmission failed. Returned:\n\t"+ioe.getMessage(), EMsgType.FAIL);
|
logPrinter.print("NET: File transmission failed. Returned:\n\t"+e.getMessage(), EMsgType.FAIL);
|
||||||
logPrinter.update(file, EFileStatus.FAILED);
|
logPrinter.update(reqFile, EFileStatus.FAILED);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -403,7 +480,7 @@ public class NETCommunications extends Task<Void> { // todo: thows IOException?
|
||||||
logPrinter.print("NET: Closing server socket.", EMsgType.PASS);
|
logPrinter.print("NET: Closing server socket.", EMsgType.PASS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (IOException | NullPointerException ioe){
|
catch (IOException ioe){
|
||||||
logPrinter.print("NET: Closing server socket failed. Sometimes it's not an issue.", EMsgType.WARNING);
|
logPrinter.print("NET: Closing server socket failed. Sometimes it's not an issue.", EMsgType.WARNING);
|
||||||
}
|
}
|
||||||
if (status != null) {
|
if (status != null) {
|
|
@ -1,4 +1,4 @@
|
||||||
package nsusbloader.NET;
|
package nsusbloader.COM.NET;
|
||||||
|
|
||||||
import nsusbloader.NSLMain;
|
import nsusbloader.NSLMain;
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import javafx.application.Platform;
|
import javafx.application.Platform;
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import nsusbloader.MediatorControl;
|
import nsusbloader.MediatorControl;
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import nsusbloader.COM.Helpers.NSSplitReader;
|
||||||
import org.usb4java.DeviceHandle;
|
import org.usb4java.DeviceHandle;
|
||||||
import org.usb4java.LibUsb;
|
import org.usb4java.LibUsb;
|
||||||
|
|
||||||
|
@ -19,14 +19,10 @@ import java.util.*;
|
||||||
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.CompletableFuture;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* GoldLeaf processing
|
* GoldLeaf 0.7 - 0.7.3 processing
|
||||||
*/
|
*/
|
||||||
class GoldLeaf implements ITransferModule {
|
class GoldLeaf extends TransferModule {
|
||||||
private LogPrinter logPrinter;
|
|
||||||
private DeviceHandle handlerNS;
|
|
||||||
private LinkedHashMap<String, File> nspMap;
|
|
||||||
private boolean nspFilterForGl;
|
private boolean nspFilterForGl;
|
||||||
private Task<Void> task;
|
|
||||||
|
|
||||||
// CMD
|
// CMD
|
||||||
private final byte[] CMD_GLCO_SUCCESS = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD
|
private final byte[] CMD_GLCO_SUCCESS = new byte[]{0x47, 0x4c, 0x43, 0x4F, 0x00, 0x00, 0x00, 0x00}; // used @ writeToUsb_GLCMD
|
||||||
|
@ -44,8 +40,11 @@ class GoldLeaf implements ITransferModule {
|
||||||
|
|
||||||
private String openReadFileNameAndPath;
|
private String openReadFileNameAndPath;
|
||||||
private RandomAccessFile randAccessFile;
|
private RandomAccessFile randAccessFile;
|
||||||
|
private NSSplitReader splitReader;
|
||||||
|
|
||||||
private HashMap<String, BufferedOutputStream> writeFilesMap;
|
private HashMap<String, BufferedOutputStream> writeFilesMap;
|
||||||
|
private long virtDriveSize;
|
||||||
|
private HashMap<String, Long> splitFileSize;
|
||||||
|
|
||||||
private boolean isWindows;
|
private boolean isWindows;
|
||||||
private String homePath;
|
private String homePath;
|
||||||
|
@ -53,6 +52,8 @@ class GoldLeaf implements ITransferModule {
|
||||||
private File selectedFile;
|
private File selectedFile;
|
||||||
|
|
||||||
GoldLeaf(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter, boolean nspFilter){
|
GoldLeaf(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter, boolean nspFilter){
|
||||||
|
super(handler, nspMap, task, logPrinter);
|
||||||
|
|
||||||
final byte CMD_GetDriveCount = 0x00;
|
final byte CMD_GetDriveCount = 0x00;
|
||||||
final byte CMD_GetDriveInfo = 0x01;
|
final byte CMD_GetDriveInfo = 0x01;
|
||||||
final byte CMD_StatPath = 0x02; // proxy done [proxy: in case if folder contains ENG+RUS+UKR file names works incorrect]
|
final byte CMD_StatPath = 0x02; // proxy done [proxy: in case if folder contains ENG+RUS+UKR file names works incorrect]
|
||||||
|
@ -72,11 +73,7 @@ class GoldLeaf implements ITransferModule {
|
||||||
|
|
||||||
final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49};
|
final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49};
|
||||||
|
|
||||||
this.handlerNS = handler;
|
|
||||||
this.nspMap = nspMap;
|
|
||||||
this.logPrinter = logPrinter;
|
|
||||||
this.nspFilterForGl = nspFilter;
|
this.nspFilterForGl = nspFilter;
|
||||||
this.task = task;
|
|
||||||
|
|
||||||
logPrinter.print("============= GoldLeaf =============\n\tVIRT:/ equals files added into the application\n\tHOME:/ equals "
|
logPrinter.print("============= GoldLeaf =============\n\tVIRT:/ equals files added into the application\n\tHOME:/ equals "
|
||||||
+System.getProperty("user.home"), EMsgType.INFO);
|
+System.getProperty("user.home"), EMsgType.INFO);
|
||||||
|
@ -92,6 +89,22 @@ class GoldLeaf implements ITransferModule {
|
||||||
|
|
||||||
homePath = System.getProperty("user.home")+File.separator;
|
homePath = System.getProperty("user.home")+File.separator;
|
||||||
|
|
||||||
|
splitFileSize = new HashMap<>();
|
||||||
|
|
||||||
|
// Calculate size of VIRT:/ drive
|
||||||
|
for (File nspFile : nspMap.values()){
|
||||||
|
if (nspFile.isDirectory()) {
|
||||||
|
File[] subFiles = nspFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||||
|
long size = 0;
|
||||||
|
for (File subFile : subFiles) // Validated by parent class
|
||||||
|
size += subFile.length();
|
||||||
|
virtDriveSize += size;
|
||||||
|
splitFileSize.put(nspFile.getName(), size);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
virtDriveSize += nspFile.length();
|
||||||
|
}
|
||||||
|
|
||||||
// Go parse commands
|
// Go parse commands
|
||||||
byte[] readByte;
|
byte[] readByte;
|
||||||
int someLength1,
|
int someLength1,
|
||||||
|
@ -193,7 +206,7 @@ class GoldLeaf implements ITransferModule {
|
||||||
for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
|
for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){
|
||||||
try{
|
try{
|
||||||
fBufOutStream.close();
|
fBufOutStream.close();
|
||||||
}catch (IOException ignored){}
|
}catch (IOException | NullPointerException ignored){}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
closeOpenedReadFilesGl();
|
closeOpenedReadFilesGl();
|
||||||
|
@ -207,9 +220,14 @@ class GoldLeaf implements ITransferModule {
|
||||||
try{
|
try{
|
||||||
randAccessFile.close();
|
randAccessFile.close();
|
||||||
}
|
}
|
||||||
catch (IOException ignored){}
|
catch (IOException | NullPointerException ignored){}
|
||||||
|
try{
|
||||||
|
splitReader.close();
|
||||||
|
}
|
||||||
|
catch (IOException | NullPointerException ignored){}
|
||||||
openReadFileNameAndPath = null;
|
openReadFileNameAndPath = null;
|
||||||
randAccessFile = null;
|
randAccessFile = null;
|
||||||
|
splitReader = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
|
@ -243,7 +261,7 @@ class GoldLeaf implements ITransferModule {
|
||||||
driveLetterLen,
|
driveLetterLen,
|
||||||
totalFreeSpace,
|
totalFreeSpace,
|
||||||
totalSize;
|
totalSize;
|
||||||
long totalSizeLong = 0;
|
long totalSizeLong;
|
||||||
|
|
||||||
// 0 == VIRTUAL DRIVE
|
// 0 == VIRTUAL DRIVE
|
||||||
if (driveNo == 0){
|
if (driveNo == 0){
|
||||||
|
@ -252,9 +270,7 @@ class GoldLeaf implements ITransferModule {
|
||||||
driveLetter = "VIRT".getBytes(StandardCharsets.UTF_16LE); // TODO: Consider moving to class field declaration
|
driveLetter = "VIRT".getBytes(StandardCharsets.UTF_16LE); // TODO: Consider moving to class field declaration
|
||||||
driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
|
driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7
|
||||||
totalFreeSpace = new byte[4];
|
totalFreeSpace = new byte[4];
|
||||||
for (File nspFile : nspMap.values()){
|
totalSizeLong = virtDriveSize;
|
||||||
totalSizeLong += nspFile.length();
|
|
||||||
}
|
|
||||||
totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
|
totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL!
|
||||||
}
|
}
|
||||||
else { //1 == User home dir
|
else { //1 == User home dir
|
||||||
|
@ -567,7 +583,12 @@ class GoldLeaf implements ITransferModule {
|
||||||
filePath = filePath.replaceFirst("VIRT:/", "");
|
filePath = filePath.replaceFirst("VIRT:/", "");
|
||||||
if (nspMap.containsKey(filePath)){
|
if (nspMap.containsKey(filePath)){
|
||||||
command.add(GL_OBJ_TYPE_FILE); // THIS IS INT
|
command.add(GL_OBJ_TYPE_FILE); // THIS IS INT
|
||||||
command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
|
if (nspMap.get(filePath).isDirectory()) {
|
||||||
|
command.add(longToArrLE(splitFileSize.get(filePath))); // YES, THIS IS LONG!;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
command.add(longToArrLE(nspMap.get(filePath).length())); // YES, THIS IS LONG!
|
||||||
|
|
||||||
if (writeGL_PASS(command)) {
|
if (writeGL_PASS(command)) {
|
||||||
logPrinter.print("GL Handle 'StatPath' command.", EMsgType.FAIL);
|
logPrinter.print("GL Handle 'StatPath' command.", EMsgType.FAIL);
|
||||||
return true;
|
return true;
|
||||||
|
@ -693,7 +714,7 @@ class GoldLeaf implements ITransferModule {
|
||||||
* false if everything is ok
|
* false if everything is ok
|
||||||
* */
|
* */
|
||||||
private boolean readFile(String fileName, long offset, long size) {
|
private boolean readFile(String fileName, long offset, long size) {
|
||||||
//System.out.println("readFile "+fileName+" "+offset+" "+size);
|
//System.out.println("readFile "+fileName+" "+offset+" "+size+"\n");
|
||||||
if (fileName.startsWith("VIRT:/")){
|
if (fileName.startsWith("VIRT:/")){
|
||||||
// Let's find out which file requested
|
// Let's find out which file requested
|
||||||
String fNamePath = nspMap.get(fileName.substring(6)).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
|
String fNamePath = nspMap.get(fileName.substring(6)).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length
|
||||||
|
@ -703,14 +724,25 @@ class GoldLeaf implements ITransferModule {
|
||||||
if (openReadFileNameAndPath != null){
|
if (openReadFileNameAndPath != null){
|
||||||
try{
|
try{
|
||||||
randAccessFile.close();
|
randAccessFile.close();
|
||||||
}catch (IOException ignored){}
|
}catch (Exception ignored){}
|
||||||
|
try{
|
||||||
|
splitReader.close();
|
||||||
|
}catch (Exception ignored){}
|
||||||
}
|
}
|
||||||
// Open what has to be opened
|
// Open what has to be opened
|
||||||
try{
|
try{
|
||||||
randAccessFile = new RandomAccessFile(nspMap.get(fileName.substring(6)), "r");
|
File tempFile = nspMap.get(fileName.substring(6));
|
||||||
|
if (tempFile.isDirectory()) {
|
||||||
|
randAccessFile = null;
|
||||||
|
splitReader = new NSSplitReader(tempFile, 0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
splitReader = null;
|
||||||
|
randAccessFile = new RandomAccessFile(tempFile, "r");
|
||||||
|
}
|
||||||
openReadFileNameAndPath = fNamePath;
|
openReadFileNameAndPath = fNamePath;
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (IOException | NullPointerException ioe){
|
||||||
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -724,47 +756,79 @@ class GoldLeaf implements ITransferModule {
|
||||||
if (openReadFileNameAndPath != null){
|
if (openReadFileNameAndPath != null){
|
||||||
try{
|
try{
|
||||||
randAccessFile.close();
|
randAccessFile.close();
|
||||||
}catch (IOException ignored){}
|
}catch (IOException | NullPointerException ignored){}
|
||||||
}
|
}
|
||||||
// Open what has to be opened
|
// Open what has to be opened
|
||||||
try{
|
try{
|
||||||
randAccessFile = new RandomAccessFile(fileName, "r");
|
randAccessFile = new RandomAccessFile(fileName, "r");
|
||||||
openReadFileNameAndPath = fileName;
|
openReadFileNameAndPath = fileName;
|
||||||
}catch (IOException ioe){
|
}catch (IOException | NullPointerException ioe){
|
||||||
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//----------------------- Actual transfer chain ------------------------
|
//----------------------- Actual transfer chain ------------------------
|
||||||
try{
|
try{
|
||||||
randAccessFile.seek(offset);
|
if (randAccessFile == null){
|
||||||
byte[] chunk = new byte[(int)size]; // WTF MAN?
|
splitReader.seek(offset);
|
||||||
// Let's find out how much bytes we got
|
byte[] chunk = new byte[(int)size]; // WTF MAN?
|
||||||
int bytesRead = randAccessFile.read(chunk);
|
// Let's find out how much bytes we got
|
||||||
// Let's check that we read expected size
|
int bytesRead = splitReader.read(chunk);
|
||||||
if (bytesRead != (int)size)
|
// Let's check that we read expected size
|
||||||
return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
|
if (bytesRead != (int)size)
|
||||||
// Let's tell as a command about our result.
|
return writeGL_FAIL("GL Handle 'ReadFile' command [CMD]\n" +
|
||||||
if (writeGL_PASS(longToArrLE(size))) {
|
" At offset: "+offset+"\n Requested: "+size+"\n Received: "+bytesRead);
|
||||||
logPrinter.print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
|
// Let's tell as a command about our result.
|
||||||
return true;
|
if (writeGL_PASS(longToArrLE(size))) {
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Let's bypass bytes we read total
|
||||||
|
if (writeToUsb(chunk)) {
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
// Let's bypass bytes we read total
|
else {
|
||||||
if (writeToUsb(chunk)) {
|
randAccessFile.seek(offset);
|
||||||
logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL);
|
byte[] chunk = new byte[(int)size]; // WTF MAN?
|
||||||
return true;
|
// Let's find out how much bytes we got
|
||||||
|
int bytesRead = randAccessFile.read(chunk);
|
||||||
|
// Let's check that we read expected size
|
||||||
|
if (bytesRead != (int)size)
|
||||||
|
return writeGL_FAIL("GL Handle 'ReadFile' command [CMD] Requested = "+size+" Read from file = "+bytesRead);
|
||||||
|
// Let's tell as a command about our result.
|
||||||
|
if (writeGL_PASS(longToArrLE(size))) {
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command [CMD]", EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// Let's bypass bytes we read total
|
||||||
|
if (writeToUsb(chunk)) {
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (Exception ioe){
|
||||||
try{
|
try{
|
||||||
randAccessFile.close();
|
randAccessFile.close();
|
||||||
}
|
}
|
||||||
catch (IOException ioee){
|
catch (NullPointerException ignored){}
|
||||||
logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioee.getMessage(), EMsgType.WARNING);
|
catch (IOException ioe_){
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
|
||||||
|
}
|
||||||
|
try{
|
||||||
|
splitReader.close();
|
||||||
|
}
|
||||||
|
catch (NullPointerException ignored){}
|
||||||
|
catch (IOException ioe_){
|
||||||
|
logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioe_.getMessage(), EMsgType.WARNING);
|
||||||
}
|
}
|
||||||
openReadFileNameAndPath = null;
|
openReadFileNameAndPath = null;
|
||||||
randAccessFile = null;
|
randAccessFile = null;
|
||||||
|
splitReader = null;
|
||||||
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -852,10 +916,6 @@ class GoldLeaf implements ITransferModule {
|
||||||
return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
|
return writeGL_FAIL("GL Handle 'SelectFile' command: Nothing selected");
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EFileStatus getStatus() {
|
|
||||||
return EFileStatus.UNKNOWN;
|
|
||||||
}
|
|
||||||
/*----------------------------------------------------*/
|
/*----------------------------------------------------*/
|
||||||
/* GL HELPERS */
|
/* GL HELPERS */
|
||||||
/*----------------------------------------------------*/
|
/*----------------------------------------------------*/
|
|
@ -1,10 +1,11 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
import nsusbloader.USB.PFS.PFSProvider;
|
import nsusbloader.COM.Helpers.NSSplitReader;
|
||||||
|
import nsusbloader.COM.USB.PFS.PFSProvider;
|
||||||
import org.usb4java.DeviceHandle;
|
import org.usb4java.DeviceHandle;
|
||||||
import org.usb4java.LibUsb;
|
import org.usb4java.LibUsb;
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ import java.util.LinkedHashMap;
|
||||||
/**
|
/**
|
||||||
* GoldLeaf processing
|
* GoldLeaf processing
|
||||||
* */
|
* */
|
||||||
public class GoldLeaf_05 implements ITransferModule{
|
public class GoldLeaf_05 extends TransferModule {
|
||||||
// CMD G L U C
|
// CMD G L U C
|
||||||
private static final byte[] CMD_GLUC = new byte[]{0x47, 0x4c, 0x55, 0x43};
|
private static final byte[] CMD_GLUC = new byte[]{0x47, 0x4c, 0x55, 0x43};
|
||||||
private static final byte[] CMD_ConnectionRequest = new byte[]{0x00, 0x00, 0x00, 0x00}; // Write-only command
|
private static final byte[] CMD_ConnectionRequest = new byte[]{0x00, 0x00, 0x00, 0x00}; // Write-only command
|
||||||
|
@ -31,13 +32,13 @@ public class GoldLeaf_05 implements ITransferModule{
|
||||||
private static final byte[] CMD_NSPTicket = new byte[]{0x06, 0x00, 0x00, 0x00};
|
private static final byte[] CMD_NSPTicket = new byte[]{0x06, 0x00, 0x00, 0x00};
|
||||||
private static final byte[] CMD_Finish = new byte[]{0x07, 0x00, 0x00, 0x00};
|
private static final byte[] CMD_Finish = new byte[]{0x07, 0x00, 0x00, 0x00};
|
||||||
|
|
||||||
private DeviceHandle handlerNS;
|
|
||||||
private Task<Void> task;
|
|
||||||
private LogPrinter logPrinter;
|
|
||||||
private EFileStatus status = EFileStatus.FAILED;
|
|
||||||
private RandomAccessFile raf; // NSP File
|
private RandomAccessFile raf; // NSP File
|
||||||
|
private NSSplitReader nsr; // It'a also NSP File
|
||||||
|
|
||||||
GoldLeaf_05(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter){
|
GoldLeaf_05(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter){
|
||||||
|
super(handler, nspMap, task, logPrinter);
|
||||||
|
status = EFileStatus.FAILED;
|
||||||
|
|
||||||
logPrinter.print("============= GoldLeaf v0.5 =============\n" +
|
logPrinter.print("============= GoldLeaf v0.5 =============\n" +
|
||||||
" Only one file per time could be sent. In case you selected more the first one would be picked.", EMsgType.INFO);
|
" Only one file per time could be sent. In case you selected more the first one would be picked.", EMsgType.INFO);
|
||||||
if (nspMap.isEmpty()){
|
if (nspMap.isEmpty()){
|
||||||
|
@ -62,14 +63,14 @@ public class GoldLeaf_05 implements ITransferModule{
|
||||||
}
|
}
|
||||||
logPrinter.print("GL File structure validated and it will be uploaded", EMsgType.PASS);
|
logPrinter.print("GL File structure validated and it will be uploaded", EMsgType.PASS);
|
||||||
|
|
||||||
this.handlerNS = handler;
|
|
||||||
this.task = task;
|
|
||||||
this.logPrinter = logPrinter;
|
|
||||||
try{
|
try{
|
||||||
this.raf = new RandomAccessFile(nspFile, "r");
|
if (nspFile.isDirectory())
|
||||||
|
this.nsr = new NSSplitReader(nspFile, 0);
|
||||||
|
else
|
||||||
|
this.raf = new RandomAccessFile(nspFile, "r");
|
||||||
}
|
}
|
||||||
catch (FileNotFoundException fnfe){
|
catch (IOException ioe){
|
||||||
logPrinter.print("GL File not found\n\t"+fnfe.getMessage(), EMsgType.FAIL);
|
logPrinter.print("GL File not found\n\t"+ioe.getMessage(), EMsgType.FAIL);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -131,9 +132,11 @@ public class GoldLeaf_05 implements ITransferModule{
|
||||||
try {
|
try {
|
||||||
raf.close();
|
raf.close();
|
||||||
}
|
}
|
||||||
catch (IOException ioe){
|
catch (IOException | NullPointerException ignored){}
|
||||||
logPrinter.print("GL Failed to close file.", EMsgType.INFO);
|
try {
|
||||||
|
nsr.close();
|
||||||
}
|
}
|
||||||
|
catch (IOException | NullPointerException ignored){}
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* ConnectionResponse command handler
|
* ConnectionResponse command handler
|
||||||
|
@ -254,21 +257,41 @@ public class GoldLeaf_05 implements ITransferModule{
|
||||||
byte[] readBuf;
|
byte[] readBuf;
|
||||||
|
|
||||||
try{
|
try{
|
||||||
raf.seek(realNcaOffset);
|
if (raf == null){
|
||||||
|
nsr.seek(realNcaOffset);
|
||||||
|
|
||||||
while (readFrom < realNcaSize){
|
while (readFrom < realNcaSize){
|
||||||
if (realNcaSize - readFrom < readPice)
|
if (realNcaSize - readFrom < readPice)
|
||||||
readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee
|
readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee
|
||||||
readBuf = new byte[readPice];
|
readBuf = new byte[readPice];
|
||||||
if (raf.read(readBuf) != readPice)
|
if (nsr.read(readBuf) != readPice)
|
||||||
return true;
|
return true;
|
||||||
//System.out.println("S: "+readFrom+" T: "+realNcaSize+" P: "+readPice); // DEBUG
|
//System.out.println("S: "+readFrom+" T: "+realNcaSize+" P: "+readPice); // DEBUG
|
||||||
if (writeUsb(readBuf))
|
if (writeUsb(readBuf))
|
||||||
return true;
|
return true;
|
||||||
//-----------------------------------------/
|
//-----------------------------------------/
|
||||||
logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
|
logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
|
||||||
//-----------------------------------------/
|
//-----------------------------------------/
|
||||||
readFrom += readPice;
|
readFrom += readPice;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
raf.seek(realNcaOffset);
|
||||||
|
|
||||||
|
while (readFrom < realNcaSize){
|
||||||
|
if (realNcaSize - readFrom < readPice)
|
||||||
|
readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee
|
||||||
|
readBuf = new byte[readPice];
|
||||||
|
if (raf.read(readBuf) != readPice)
|
||||||
|
return true;
|
||||||
|
//System.out.println("S: "+readFrom+" T: "+realNcaSize+" P: "+readPice); // DEBUG
|
||||||
|
if (writeUsb(readBuf))
|
||||||
|
return true;
|
||||||
|
//-----------------------------------------/
|
||||||
|
logPrinter.updateProgress((readFrom+readPice)/(realNcaSize/100.0) / 100.0);
|
||||||
|
//-----------------------------------------/
|
||||||
|
readFrom += readPice;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
//-----------------------------------------/
|
//-----------------------------------------/
|
||||||
logPrinter.updateProgress(1.0);
|
logPrinter.updateProgress(1.0);
|
||||||
|
@ -282,8 +305,6 @@ public class GoldLeaf_05 implements ITransferModule{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public EFileStatus getStatus() { return status; }
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sending any byte array to USB device
|
* Sending any byte array to USB device
|
|
@ -1,4 +1,4 @@
|
||||||
package nsusbloader.USB.PFS;
|
package nsusbloader.COM.USB.PFS;
|
||||||
|
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.nio.ByteOrder;
|
import java.nio.ByteOrder;
|
|
@ -1,8 +1,7 @@
|
||||||
package nsusbloader.USB.PFS;
|
package nsusbloader.COM.USB.PFS;
|
||||||
|
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
import nsusbloader.ServiceWindow;
|
|
||||||
|
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -22,9 +21,14 @@ public class PFSProvider {
|
||||||
private int ticketID = -1;
|
private int ticketID = -1;
|
||||||
|
|
||||||
public PFSProvider(File nspFile, LogPrinter logPrinter) throws Exception{
|
public PFSProvider(File nspFile, LogPrinter logPrinter) throws Exception{
|
||||||
|
if (nspFile.isDirectory()) {
|
||||||
|
nspFileName = nspFile.getName();
|
||||||
|
nspFile = new File(nspFile.getAbsolutePath() + File.separator + "00");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
nspFileName = nspFile.getName();
|
||||||
|
|
||||||
RandomAccessFile randAccessFile = new RandomAccessFile(nspFile, "r");
|
RandomAccessFile randAccessFile = new RandomAccessFile(nspFile, "r");
|
||||||
nspFileName = nspFile.getName();
|
|
||||||
|
|
||||||
int filesCount;
|
int filesCount;
|
||||||
int header;
|
int header;
|
|
@ -1,9 +1,10 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import nsusbloader.COM.Helpers.NSSplitReader;
|
||||||
import org.usb4java.DeviceHandle;
|
import org.usb4java.DeviceHandle;
|
||||||
import org.usb4java.LibUsb;
|
import org.usb4java.LibUsb;
|
||||||
|
|
||||||
|
@ -18,18 +19,9 @@ import java.util.LinkedHashMap;
|
||||||
/**
|
/**
|
||||||
* Tinfoil processing
|
* Tinfoil processing
|
||||||
* */
|
* */
|
||||||
class TinFoil implements ITransferModule {
|
class TinFoil extends TransferModule {
|
||||||
private LogPrinter logPrinter;
|
|
||||||
private DeviceHandle handlerNS;
|
|
||||||
private LinkedHashMap<String, File> nspMap;
|
|
||||||
private EFileStatus status = EFileStatus.FAILED;
|
|
||||||
private Task<Void> task;
|
|
||||||
|
|
||||||
TinFoil(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter){
|
TinFoil(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter logPrinter){
|
||||||
this.handlerNS = handler;
|
super(handler, nspMap, task, logPrinter);
|
||||||
this.nspMap = nspMap;
|
|
||||||
this.task = task;
|
|
||||||
this.logPrinter = logPrinter;
|
|
||||||
|
|
||||||
logPrinter.print("============= TinFoil =============", EMsgType.INFO);
|
logPrinter.print("============= TinFoil =============", EMsgType.INFO);
|
||||||
|
|
||||||
|
@ -162,44 +154,83 @@ class TinFoil implements ITransferModule {
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
byte[] bufferCurrent; //= new byte[1048576]; // eq. Allocate 1mb
|
||||||
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
|
|
||||||
byte[] bufferCurrent ;//= new byte[1048576]; // eq. Allocate 1mb
|
|
||||||
|
|
||||||
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset){
|
|
||||||
logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
long currentOffset = 0;
|
long currentOffset = 0;
|
||||||
// 'End Offset' equal to receivedRangeSize.
|
// 'End Offset' equal to receivedRangeSize.
|
||||||
int readPice = 8388608; // = 8Mb
|
int readPice = 8388608; // = 8Mb
|
||||||
|
|
||||||
while (currentOffset < receivedRangeSize){
|
//---------------! Split files start !---------------
|
||||||
if ((currentOffset + readPice) >= receivedRangeSize )
|
if (nspMap.get(receivedRequestedNSP).isDirectory()){
|
||||||
readPice = Math.toIntExact(receivedRangeSize - currentOffset);
|
NSSplitReader nsSplitReader = new NSSplitReader(nspMap.get(receivedRequestedNSP), receivedRangeSize);
|
||||||
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // NOTE: DEBUG
|
if (nsSplitReader.seek(receivedRangeOffset) != receivedRangeOffset){
|
||||||
// updating progress bar (if a lot of data requested) START BLOCK
|
logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
|
||||||
//-----------------------------------------/
|
return false;
|
||||||
logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
|
}
|
||||||
//-----------------------------------------/
|
|
||||||
bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
|
||||||
|
|
||||||
if (bufferedInStream.read(bufferCurrent) != readPice) { // changed since @ v0.3.2
|
while (currentOffset < receivedRangeSize){
|
||||||
logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
|
if ((currentOffset + readPice) >= receivedRangeSize )
|
||||||
return false;
|
readPice = Math.toIntExact(receivedRangeSize - currentOffset);
|
||||||
|
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // NOTE: DEBUG
|
||||||
|
// updating progress bar (if a lot of data requested) START BLOCK
|
||||||
|
//---Tell progress to UI---/
|
||||||
|
logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0);
|
||||||
|
//------------------------/
|
||||||
|
bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
||||||
|
|
||||||
|
if (nsSplitReader.read(bufferCurrent) != readPice) { // changed since @ v0.3.2
|
||||||
|
logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//write to USB
|
||||||
|
if (writeUsb(bufferCurrent)) {
|
||||||
|
logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
currentOffset += readPice;
|
||||||
}
|
}
|
||||||
//write to USB
|
nsSplitReader.close();
|
||||||
if (writeUsb(bufferCurrent)) {
|
//---Tell progress to UI---/
|
||||||
logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
|
logPrinter.updateProgress(1.0);
|
||||||
return false;
|
//------------------------/
|
||||||
}
|
|
||||||
currentOffset += readPice;
|
|
||||||
}
|
}
|
||||||
bufferedInStream.close();
|
//---------------! Split files end !---------------
|
||||||
//-----------------------------------------/
|
//---------------! Regular files start !---------------
|
||||||
logPrinter.updateProgress(1.0);
|
else {
|
||||||
//-----------------------------------------/
|
BufferedInputStream bufferedInStream = new BufferedInputStream(new FileInputStream(nspMap.get(receivedRequestedNSP))); // TODO: refactor?
|
||||||
|
|
||||||
|
if (bufferedInStream.skip(receivedRangeOffset) != receivedRangeOffset) {
|
||||||
|
logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (currentOffset < receivedRangeSize) {
|
||||||
|
if ((currentOffset + readPice) >= receivedRangeSize)
|
||||||
|
readPice = Math.toIntExact(receivedRangeSize - currentOffset);
|
||||||
|
//System.out.println("CO: "+currentOffset+"\t\tEO: "+receivedRangeSize+"\t\tRP: "+readPice); // NOTE: DEBUG
|
||||||
|
// updating progress bar (if a lot of data requested) START BLOCK
|
||||||
|
//---Tell progress to UI---/
|
||||||
|
logPrinter.updateProgress((currentOffset + readPice) / (receivedRangeSize / 100.0) / 100.0);
|
||||||
|
//------------------------/
|
||||||
|
bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring.
|
||||||
|
|
||||||
|
if (bufferedInStream.read(bufferCurrent) != readPice) { // changed since @ v0.3.2
|
||||||
|
logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//write to USB
|
||||||
|
if (writeUsb(bufferCurrent)) {
|
||||||
|
logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
currentOffset += readPice;
|
||||||
|
}
|
||||||
|
bufferedInStream.close();
|
||||||
|
//---Tell progress to UI---/
|
||||||
|
logPrinter.updateProgress(1.0);
|
||||||
|
//------------------------/
|
||||||
|
}
|
||||||
|
//---------------! Regular files end !---------------
|
||||||
} catch (FileNotFoundException fnfe){
|
} catch (FileNotFoundException fnfe){
|
||||||
logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL);
|
logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL);
|
||||||
fnfe.printStackTrace();
|
fnfe.printStackTrace();
|
||||||
|
@ -212,6 +243,10 @@ class TinFoil implements ITransferModule {
|
||||||
logPrinter.print("TF ArithmeticException (can't cast 'offset end' - 'offsets current' to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL);
|
logPrinter.print("TF ArithmeticException (can't cast 'offset end' - 'offsets current' to 'integer'):\n "+ae.getMessage(), EMsgType.FAIL);
|
||||||
ae.printStackTrace();
|
ae.printStackTrace();
|
||||||
return false;
|
return false;
|
||||||
|
} catch (NullPointerException npe){
|
||||||
|
logPrinter.print("TF NullPointerException (in some moment application didn't find something. Something important.):\n "+npe.getMessage(), EMsgType.FAIL);
|
||||||
|
npe.printStackTrace();
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -309,13 +344,4 @@ class TinFoil implements ITransferModule {
|
||||||
logPrinter.print("TF Execution interrupted", EMsgType.INFO);
|
logPrinter.print("TF Execution interrupted", EMsgType.INFO);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Status getter
|
|
||||||
* @return status
|
|
||||||
*/
|
|
||||||
@Override
|
|
||||||
public EFileStatus getStatus() {
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
}
|
54
src/main/java/nsusbloader/COM/USB/TransferModule.java
Normal file
54
src/main/java/nsusbloader/COM/USB/TransferModule.java
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
|
import javafx.concurrent.Task;
|
||||||
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
|
import nsusbloader.NSLDataTypes.EMsgType;
|
||||||
|
import org.usb4java.DeviceHandle;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.*;
|
||||||
|
|
||||||
|
public abstract class TransferModule {
|
||||||
|
EFileStatus status = EFileStatus.UNKNOWN;
|
||||||
|
|
||||||
|
LinkedHashMap<String, File> nspMap;
|
||||||
|
LogPrinter logPrinter;
|
||||||
|
DeviceHandle handlerNS;
|
||||||
|
Task<Void> task;
|
||||||
|
|
||||||
|
TransferModule(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> task, LogPrinter printer){
|
||||||
|
this.handlerNS = handler;
|
||||||
|
this.nspMap = nspMap;
|
||||||
|
this.task = task;
|
||||||
|
this.logPrinter = printer;
|
||||||
|
|
||||||
|
// Validate split files to be sure that there is no crap
|
||||||
|
logPrinter.print("TransferModule: Validating split files ...", EMsgType.INFO);
|
||||||
|
Iterator<Map.Entry<String, File>> iterator = nspMap.entrySet().iterator();
|
||||||
|
while (iterator.hasNext()){
|
||||||
|
File f = iterator.next().getValue();
|
||||||
|
if (f.isDirectory()){
|
||||||
|
File[] subFiles = f.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||||
|
if (subFiles == null || subFiles.length == 0) {
|
||||||
|
logPrinter.print("TransferModule: Removing empty folder: " + f.getName(), EMsgType.WARNING);
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
Arrays.sort(subFiles, Comparator.comparingInt(file -> Integer.parseInt(file.getName())));
|
||||||
|
|
||||||
|
for (int i = subFiles.length - 2; i > 0 ; i--){
|
||||||
|
if (subFiles[i].length() < subFiles[i-1].length()) {
|
||||||
|
logPrinter.print("TransferModule: Removing strange split file: "+f.getName()+
|
||||||
|
"\n (Chunk sizes of the split file are not the same, but has to be.)", EMsgType.WARNING);
|
||||||
|
iterator.remove();
|
||||||
|
} // what
|
||||||
|
} // a
|
||||||
|
} // nice
|
||||||
|
} // stairway
|
||||||
|
} // here =)
|
||||||
|
logPrinter.print("TransferModule: Validation complete.", EMsgType.INFO);
|
||||||
|
}
|
||||||
|
|
||||||
|
public EFileStatus getStatus(){ return status; }
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import javafx.concurrent.Task;
|
import javafx.concurrent.Task;
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
|
@ -50,7 +50,7 @@ public class UsbCommunications extends Task<Void> {
|
||||||
|
|
||||||
DeviceHandle handler = usbConnect.getHandlerNS();
|
DeviceHandle handler = usbConnect.getHandlerNS();
|
||||||
|
|
||||||
ITransferModule module;
|
TransferModule module;
|
||||||
|
|
||||||
if (protocol.equals("TinFoil"))
|
if (protocol.equals("TinFoil"))
|
||||||
module = new TinFoil(handler, nspMap, this, logPrinter);
|
module = new TinFoil(handler, nspMap, this, logPrinter);
|
|
@ -1,4 +1,4 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import nsusbloader.ModelControllers.LogPrinter;
|
import nsusbloader.ModelControllers.LogPrinter;
|
||||||
import nsusbloader.NSLDataTypes.EMsgType;
|
import nsusbloader.NSLDataTypes.EMsgType;
|
|
@ -1,4 +1,4 @@
|
||||||
package nsusbloader.USB;
|
package nsusbloader.COM.USB;
|
||||||
|
|
||||||
import org.usb4java.LibUsb;
|
import org.usb4java.LibUsb;
|
||||||
|
|
|
@ -8,15 +8,15 @@ import javafx.scene.control.*;
|
||||||
import javafx.scene.input.DragEvent;
|
import javafx.scene.input.DragEvent;
|
||||||
import javafx.scene.input.TransferMode;
|
import javafx.scene.input.TransferMode;
|
||||||
import javafx.scene.layout.Region;
|
import javafx.scene.layout.Region;
|
||||||
|
import javafx.stage.DirectoryChooser;
|
||||||
import javafx.stage.FileChooser;
|
import javafx.stage.FileChooser;
|
||||||
import nsusbloader.*;
|
import nsusbloader.*;
|
||||||
import nsusbloader.ModelControllers.UpdatesChecker;
|
import nsusbloader.ModelControllers.UpdatesChecker;
|
||||||
import nsusbloader.NET.NETCommunications;
|
import nsusbloader.COM.NET.NETCommunications;
|
||||||
import nsusbloader.USB.UsbCommunications;
|
import nsusbloader.COM.USB.UsbCommunications;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.util.ArrayList;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
@ -28,9 +28,8 @@ public class NSLMainController implements Initializable {
|
||||||
@FXML
|
@FXML
|
||||||
public TextArea logArea; // Accessible from Mediator
|
public TextArea logArea; // Accessible from Mediator
|
||||||
@FXML
|
@FXML
|
||||||
private Button selectNspBtn;
|
private Button selectNspBtn, selectSplitNspBtn, uploadStopBtn;
|
||||||
@FXML
|
|
||||||
private Button uploadStopBtn;
|
|
||||||
private Region btnUpStopImage;
|
private Region btnUpStopImage;
|
||||||
@FXML
|
@FXML
|
||||||
public ProgressBar progressBar; // Accessible from Mediator
|
public ProgressBar progressBar; // Accessible from Mediator
|
||||||
|
@ -62,6 +61,10 @@ public class NSLMainController implements Initializable {
|
||||||
else
|
else
|
||||||
uploadStopBtn.setDisable(false);
|
uploadStopBtn.setDisable(false);
|
||||||
selectNspBtn.setOnAction(e-> selectFilesBtnAction());
|
selectNspBtn.setOnAction(e-> selectFilesBtnAction());
|
||||||
|
|
||||||
|
selectSplitNspBtn.setOnAction(e-> selectSplitBtnAction());
|
||||||
|
selectSplitNspBtn.getStyleClass().add("buttonSelect");
|
||||||
|
|
||||||
uploadStopBtn.setOnAction(e-> uploadBtnAction());
|
uploadStopBtn.setOnAction(e-> uploadBtnAction());
|
||||||
|
|
||||||
selectNspBtn.getStyleClass().add("buttonSelect");
|
selectNspBtn.getStyleClass().add("buttonSelect");
|
||||||
|
@ -101,7 +104,6 @@ public class NSLMainController implements Initializable {
|
||||||
public ResourceBundle getResourceBundle() {
|
public ResourceBundle getResourceBundle() {
|
||||||
return resourceBundle;
|
return resourceBundle;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Provide hostServices to Settings tab
|
* Provide hostServices to Settings tab
|
||||||
* */
|
* */
|
||||||
|
@ -109,7 +111,6 @@ public class NSLMainController implements Initializable {
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Functionality for selecting NSP button.
|
* Functionality for selecting NSP button.
|
||||||
* Uses setReady and setNotReady to simplify code readability.
|
|
||||||
* */
|
* */
|
||||||
private void selectFilesBtnAction(){
|
private void selectFilesBtnAction(){
|
||||||
List<File> filesList;
|
List<File> filesList;
|
||||||
|
@ -138,6 +139,28 @@ public class NSLMainController implements Initializable {
|
||||||
previouslyOpenedPath = filesList.get(0).getParent();
|
previouslyOpenedPath = filesList.get(0).getParent();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Functionality for selecting Split NSP button.
|
||||||
|
* */
|
||||||
|
private void selectSplitBtnAction(){
|
||||||
|
File splitFile;
|
||||||
|
DirectoryChooser dirChooser = new DirectoryChooser();
|
||||||
|
dirChooser.setTitle(resourceBundle.getString("btn_OpenFile"));
|
||||||
|
|
||||||
|
File validator = new File(previouslyOpenedPath);
|
||||||
|
if (validator.exists())
|
||||||
|
dirChooser.setInitialDirectory(validator);
|
||||||
|
else
|
||||||
|
dirChooser.setInitialDirectory(new File(System.getProperty("user.home")));
|
||||||
|
|
||||||
|
splitFile = dirChooser.showDialog(logArea.getScene().getWindow());
|
||||||
|
|
||||||
|
if (splitFile != null && splitFile.getName().toLowerCase().endsWith(".nsp")) {
|
||||||
|
FrontTabController.tableFilesListController.setFile(splitFile);
|
||||||
|
uploadStopBtn.setDisable(false); // Is it useful?
|
||||||
|
previouslyOpenedPath = splitFile.getParent();
|
||||||
|
}
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* It's button listener when no transmission executes
|
* It's button listener when no transmission executes
|
||||||
* */
|
* */
|
||||||
|
@ -210,6 +233,7 @@ public class NSLMainController implements Initializable {
|
||||||
public void notifyTransmissionStarted(boolean isTransmissionStarted){
|
public void notifyTransmissionStarted(boolean isTransmissionStarted){
|
||||||
if (isTransmissionStarted) {
|
if (isTransmissionStarted) {
|
||||||
selectNspBtn.setDisable(true);
|
selectNspBtn.setDisable(true);
|
||||||
|
selectSplitNspBtn.setDisable(true);
|
||||||
uploadStopBtn.setOnAction(e-> stopBtnAction());
|
uploadStopBtn.setOnAction(e-> stopBtnAction());
|
||||||
|
|
||||||
uploadStopBtn.setText(resourceBundle.getString("btn_Stop"));
|
uploadStopBtn.setText(resourceBundle.getString("btn_Stop"));
|
||||||
|
@ -222,6 +246,7 @@ public class NSLMainController implements Initializable {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
selectNspBtn.setDisable(false);
|
selectNspBtn.setDisable(false);
|
||||||
|
selectSplitNspBtn.setDisable(false);
|
||||||
uploadStopBtn.setOnAction(e-> uploadBtnAction());
|
uploadStopBtn.setOnAction(e-> uploadBtnAction());
|
||||||
|
|
||||||
uploadStopBtn.setText(resourceBundle.getString("btn_Upload"));
|
uploadStopBtn.setText(resourceBundle.getString("btn_Upload"));
|
||||||
|
@ -253,58 +278,22 @@ public class NSLMainController implements Initializable {
|
||||||
/**
|
/**
|
||||||
* Drag-n-drop support (drop consumer)
|
* Drag-n-drop support (drop consumer)
|
||||||
* */
|
* */
|
||||||
// TODO: DO SOMETHING WITH THIS
|
|
||||||
@FXML
|
@FXML
|
||||||
private void handleDrop(DragEvent event){
|
private void handleDrop(DragEvent event){
|
||||||
if (MediatorControl.getInstance().getTransferActive()) {
|
if (MediatorControl.getInstance().getTransferActive()) {
|
||||||
event.setDropCompleted(true);
|
event.setDropCompleted(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
List<File> filesDropped = new ArrayList<>();
|
List<File> filesDropped = event.getDragboard().getFiles();
|
||||||
try {
|
|
||||||
if (FrontTabController.getSelectedProtocol().equals("TinFoil") && SettingsTabController.getTfXciNszXczSupport()){
|
|
||||||
for (File fileOrDir : event.getDragboard().getFiles()) {
|
|
||||||
if (fileOrDir.isDirectory()) {
|
|
||||||
for (File file : fileOrDir.listFiles())
|
|
||||||
if ((! file.isDirectory()) && (file.getName().toLowerCase().endsWith(".nsp") ||
|
|
||||||
file.getName().toLowerCase().endsWith(".xci") ||
|
|
||||||
file.getName().toLowerCase().endsWith(".nsz") ||
|
|
||||||
file.getName().toLowerCase().endsWith(".xcz")))
|
|
||||||
filesDropped.add(file);
|
|
||||||
}
|
|
||||||
else if (fileOrDir.getName().toLowerCase().endsWith(".nsp") || fileOrDir.getName().toLowerCase().endsWith(".xci") ||
|
|
||||||
fileOrDir.getName().toLowerCase().endsWith(".nsz") || fileOrDir.getName().toLowerCase().endsWith(".xcz") )
|
|
||||||
filesDropped.add(fileOrDir);
|
|
||||||
|
|
||||||
}
|
if (FrontTabController.getSelectedProtocol().equals("TinFoil") && SettingsTabController.getTfXciNszXczSupport())
|
||||||
}// TODO: Somehow improve this double-action function in settings.
|
filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches("(.*\\.nsp$)|(.*\\.xci$)|(.*\\.nsz$)|(.*\\.xcz$)"));
|
||||||
else if (FrontTabController.getSelectedProtocol().equals("GoldLeaf") && (! SettingsTabController.getNSPFileFilterForGL())){
|
else if (FrontTabController.getSelectedProtocol().equals("GoldLeaf") && (! SettingsTabController.getNSPFileFilterForGL()))
|
||||||
for (File fileOrDir : event.getDragboard().getFiles()) {
|
filesDropped.removeIf(file -> (file.isDirectory() && ! file.getName().toLowerCase().matches(".*\\.nsp$")));
|
||||||
if (fileOrDir.isDirectory()) {
|
else
|
||||||
for (File file : fileOrDir.listFiles())
|
filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches(".*\\.nsp$"));
|
||||||
if ((! file.isDirectory()) && (! file.isHidden()))
|
|
||||||
filesDropped.add(file);
|
if ( ! filesDropped.isEmpty() )
|
||||||
}
|
|
||||||
else
|
|
||||||
filesDropped.add(fileOrDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
for (File fileOrDir : event.getDragboard().getFiles()) {
|
|
||||||
if (fileOrDir.isDirectory()){
|
|
||||||
for (File file : fileOrDir.listFiles())
|
|
||||||
if (file.getName().toLowerCase().endsWith(".nsp") && (! file.isDirectory()))
|
|
||||||
filesDropped.add(file);
|
|
||||||
}
|
|
||||||
else if (fileOrDir.getName().toLowerCase().endsWith(".nsp"))
|
|
||||||
filesDropped.add(fileOrDir);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (SecurityException se){
|
|
||||||
se.printStackTrace();
|
|
||||||
}
|
|
||||||
if (!filesDropped.isEmpty())
|
|
||||||
FrontTabController.tableFilesListController.setFiles(filesDropped);
|
FrontTabController.tableFilesListController.setFiles(filesDropped);
|
||||||
|
|
||||||
event.setDropCompleted(true);
|
event.setDropCompleted(true);
|
||||||
|
|
|
@ -3,6 +3,7 @@ package nsusbloader.Controllers;
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
import nsusbloader.NSLDataTypes.EFileStatus;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.io.FilenameFilter;
|
||||||
|
|
||||||
public class NSLRowModel {
|
public class NSLRowModel {
|
||||||
|
|
||||||
|
@ -16,7 +17,15 @@ public class NSLRowModel {
|
||||||
this.nspFile = nspFile;
|
this.nspFile = nspFile;
|
||||||
this.markForUpload = checkBoxValue;
|
this.markForUpload = checkBoxValue;
|
||||||
this.nspFileName = nspFile.getName();
|
this.nspFileName = nspFile.getName();
|
||||||
this.nspFileSize = nspFile.length();
|
if (nspFile.isFile())
|
||||||
|
this.nspFileSize = nspFile.length();
|
||||||
|
else {
|
||||||
|
File[] subFilesArr = nspFile.listFiles((file, name) -> name.matches("[0-9]{2}"));
|
||||||
|
if (subFilesArr != null) {
|
||||||
|
for (File subFile : subFilesArr)
|
||||||
|
this.nspFileSize += subFile.length();
|
||||||
|
}
|
||||||
|
}
|
||||||
this.status = "";
|
this.status = "";
|
||||||
}
|
}
|
||||||
// Model methods start
|
// Model methods start
|
||||||
|
|
|
@ -149,6 +149,24 @@ public class NSTableViewController implements Initializable {
|
||||||
table.getColumns().add(fileSizeColumn);
|
table.getColumns().add(fileSizeColumn);
|
||||||
table.getColumns().add(uploadColumn);
|
table.getColumns().add(uploadColumn);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Add single file when user selected it (Split file usually)
|
||||||
|
* */
|
||||||
|
public void setFile(File file){
|
||||||
|
if ( ! rowsObsLst.isEmpty()){
|
||||||
|
List<String> filesAlreayInList = new ArrayList<>();
|
||||||
|
for (NSLRowModel model : rowsObsLst)
|
||||||
|
filesAlreayInList.add(model.getNspFileName());
|
||||||
|
|
||||||
|
if ( ! filesAlreayInList.contains(file.getName()))
|
||||||
|
rowsObsLst.add(new NSLRowModel(file, true));
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
rowsObsLst.add(new NSLRowModel(file, true));
|
||||||
|
MediatorControl.getInstance().getContoller().disableUploadStopBtn(false);
|
||||||
|
}
|
||||||
|
table.refresh();
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Add files when user selected them
|
* Add files when user selected them
|
||||||
* */
|
* */
|
||||||
|
|
|
@ -12,7 +12,7 @@ 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.8.2";
|
public static final String appVersion = "v0.9";
|
||||||
@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"));
|
||||||
|
|
|
@ -24,6 +24,7 @@ public class RainbowHexDump {
|
||||||
System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET);
|
System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET);
|
||||||
for (byte b: byteArray)
|
for (byte b: byteArray)
|
||||||
System.out.print(String.format("%02x ", b));
|
System.out.print(String.format("%02x ", b));
|
||||||
|
//System.out.println();
|
||||||
System.out.print("\t\t\t"
|
System.out.print("\t\t\t"
|
||||||
+ new String(byteArray, StandardCharsets.UTF_8)
|
+ new String(byteArray, StandardCharsets.UTF_8)
|
||||||
+ "\n");
|
+ "\n");
|
||||||
|
|
|
@ -1,7 +0,0 @@
|
||||||
package nsusbloader.USB;
|
|
||||||
|
|
||||||
import nsusbloader.NSLDataTypes.EFileStatus;
|
|
||||||
|
|
||||||
public interface ITransferModule {
|
|
||||||
EFileStatus getStatus();
|
|
||||||
}
|
|
|
@ -57,7 +57,7 @@
|
||||||
<Insets left="5.0" right="5.0" top="2.0" />
|
<Insets left="5.0" right="5.0" top="2.0" />
|
||||||
</VBox.margin>
|
</VBox.margin>
|
||||||
</ProgressBar>
|
</ProgressBar>
|
||||||
<HBox alignment="TOP_CENTER" VBox.vgrow="NEVER">
|
<HBox alignment="TOP_CENTER" spacing="3.0" VBox.vgrow="NEVER">
|
||||||
<children>
|
<children>
|
||||||
<Button fx:id="selectNspBtn" contentDisplay="TOP" mnemonicParsing="false" prefHeight="60.0" text="%btn_OpenFile">
|
<Button fx:id="selectNspBtn" contentDisplay="TOP" mnemonicParsing="false" prefHeight="60.0" text="%btn_OpenFile">
|
||||||
<HBox.margin>
|
<HBox.margin>
|
||||||
|
@ -67,6 +67,10 @@
|
||||||
<SVGPath content="M 8,0 C 6.8954305,0 6,0.8954305 6,2 v 16 c 0,1.1 0.89,2 2,2 h 12 c 1.104569,0 2,-0.895431 2,-2 V 2 C 22,0.90484721 21.089844,0 20,0 Z m 2.1,1.2 h 7.8 C 18,1.20208 18,1.2002604 18,1.3 v 0.1 c 0,0.095833 0,0.097917 -0.1,0.1 H 10.1 C 10,1.5057292 10,1.5036458 10,1.4 V 1.3 C 10,1.20026 10,1.1981771 10.1,1.2 Z M 8,2 h 12 c 0.303385,0 0.5,0.2044271 0.5,0.5 v 12 C 20.5,14.789959 20.29836,15 20,15 H 8 C 7.7044271,15 7.5,14.803385 7.5,14.5 V 2.5 C 7.5,2.2083333 7.7122396,2 8,2 Z M 2,4 v 18 c 0,1.104569 0.8954305,2 2,2 H 20 V 22 H 4 V 4 Z m 8,12 h 8 l -4,3 z" fill="#289de8" />
|
<SVGPath content="M 8,0 C 6.8954305,0 6,0.8954305 6,2 v 16 c 0,1.1 0.89,2 2,2 h 12 c 1.104569,0 2,-0.895431 2,-2 V 2 C 22,0.90484721 21.089844,0 20,0 Z m 2.1,1.2 h 7.8 C 18,1.20208 18,1.2002604 18,1.3 v 0.1 c 0,0.095833 0,0.097917 -0.1,0.1 H 10.1 C 10,1.5057292 10,1.5036458 10,1.4 V 1.3 C 10,1.20026 10,1.1981771 10.1,1.2 Z M 8,2 h 12 c 0.303385,0 0.5,0.2044271 0.5,0.5 v 12 C 20.5,14.789959 20.29836,15 20,15 H 8 C 7.7044271,15 7.5,14.803385 7.5,14.5 V 2.5 C 7.5,2.2083333 7.7122396,2 8,2 Z M 2,4 v 18 c 0,1.104569 0.8954305,2 2,2 H 20 V 22 H 4 V 4 Z m 8,12 h 8 l -4,3 z" fill="#289de8" />
|
||||||
</graphic>
|
</graphic>
|
||||||
</Button>
|
</Button>
|
||||||
|
<Button fx:id="selectSplitNspBtn" contentDisplay="TOP" mnemonicParsing="false" prefHeight="60.0" text="%btn_OpenSplitFile">
|
||||||
|
<graphic>
|
||||||
|
<SVGPath content="M 2.4003906 2 C 1.0683906 2 0 3.1125 0 4.5 L 0 19.5 A 2.4 2.5 0 0 0 2.4003906 22 L 21.599609 22 A 2.4 2.5 0 0 0 24 19.5 L 24 7 C 24 5.6125 22.919609 4.5 21.599609 4.5 L 12 4.5 L 9.5996094 2 L 2.4003906 2 z M 13.193359 10.962891 C 14.113498 10.962891 14.814236 11.348741 15.296875 12.123047 C 15.779514 12.89388 16.021484 13.935113 16.021484 15.244141 C 16.021484 16.556641 15.779514 17.598741 15.296875 18.373047 C 14.814236 19.14388 14.113498 19.529297 13.193359 19.529297 C 12.276693 19.529297 11.575955 19.14388 11.089844 18.373047 C 10.607205 17.598741 10.365234 16.556641 10.365234 15.244141 C 10.365234 13.935113 10.607205 12.89388 11.089844 12.123047 C 11.575955 11.348741 12.276693 10.962891 13.193359 10.962891 z M 19.589844 10.962891 C 20.509983 10.962891 21.21072 11.348741 21.693359 12.123047 C 22.175998 12.89388 22.417969 13.935113 22.417969 15.244141 C 22.417969 16.556641 22.175998 17.598741 21.693359 18.373047 C 21.21072 19.14388 20.509983 19.529297 19.589844 19.529297 C 18.673177 19.529297 17.970486 19.14388 17.484375 18.373047 C 17.001736 17.598741 16.761719 16.556641 16.761719 15.244141 C 16.761719 13.935113 17.001736 12.89388 17.484375 12.123047 C 17.970486 11.348741 18.673177 10.962891 19.589844 10.962891 z M 13.193359 11.769531 C 12.613498 11.769531 12.173177 12.092448 11.871094 12.738281 C 11.56901 13.380642 11.417969 14.195964 11.417969 15.185547 C 11.417969 15.411241 11.423611 15.655599 11.4375 15.916016 C 11.451389 16.176432 11.511068 16.528212 11.615234 16.972656 L 14.412109 12.591797 C 14.235026 12.26888 14.042318 12.052517 13.833984 11.941406 C 13.629123 11.826823 13.415582 11.769531 13.193359 11.769531 z M 19.589844 11.769531 C 19.009983 11.769531 18.567708 12.092448 18.265625 12.738281 C 17.963542 13.380642 17.8125 14.195964 17.8125 15.185547 C 17.8125 15.411241 17.820095 15.655599 17.833984 15.916016 C 17.847873 16.176432 17.907552 16.528212 18.011719 16.972656 L 20.808594 12.591797 C 20.63151 12.26888 20.438802 12.052517 20.230469 11.941406 C 20.025608 11.826823 19.812066 11.769531 19.589844 11.769531 z M 14.761719 13.556641 L 11.984375 17.962891 C 12.133681 18.216363 12.305556 18.406684 12.5 18.535156 C 12.694444 18.660156 12.91276 18.722656 13.152344 18.722656 C 13.812066 18.722656 14.280816 18.355252 14.558594 17.619141 C 14.836372 16.879557 14.974609 16.059462 14.974609 15.160156 C 14.974609 14.604601 14.90408 14.07053 14.761719 13.556641 z M 21.15625 13.556641 L 18.380859 17.962891 C 18.530165 18.216363 18.70204 18.406684 18.896484 18.535156 C 19.090929 18.660156 19.307292 18.722656 19.546875 18.722656 C 20.206597 18.722656 20.675347 18.355252 20.953125 17.619141 C 21.230903 16.879557 21.371094 16.059462 21.371094 15.160156 C 21.371094 14.604601 21.298611 14.07053 21.15625 13.556641 z" fill="#289de8" />
|
||||||
|
</graphic></Button>
|
||||||
<Pane HBox.hgrow="ALWAYS" />
|
<Pane HBox.hgrow="ALWAYS" />
|
||||||
<Button fx:id="uploadStopBtn" contentDisplay="TOP" mnemonicParsing="false" prefHeight="60.0" text="%btn_Upload">
|
<Button fx:id="uploadStopBtn" contentDisplay="TOP" mnemonicParsing="false" prefHeight="60.0" text="%btn_Upload">
|
||||||
<HBox.margin>
|
<HBox.margin>
|
||||||
|
|
|
@ -38,9 +38,10 @@ windowTitleNewVersionNOTAval=No new versions available
|
||||||
windowTitleNewVersionUnknown=Unable to check for new versions
|
windowTitleNewVersionUnknown=Unable to check for new versions
|
||||||
windowBodyNewVersionUnknown=Something went wrong\nMaybe internet unavailable, or GitHub is down
|
windowBodyNewVersionUnknown=Something went wrong\nMaybe internet unavailable, or GitHub is down
|
||||||
windowBodyNewVersionNOTAval=You're using the latest version
|
windowBodyNewVersionNOTAval=You're using the latest version
|
||||||
tab2_Cb_AllowXciNszXcz=Allow XCI/NSZ/XCZ files selection for TinFoil
|
tab2_Cb_AllowXciNszXcz=Allow XCI / NSZ / XCZ files selection for TinFoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=Used by applications that support XCI/NSZ/XCZ and utilizes TinFoil transfer protocol. Don't change if not sure.
|
tab2_Lbl_AllowXciNszXczDesc=Used by applications that support XCI/NSZ/XCZ and utilizes TinFoil transfer protocol. Don't change if not sure.
|
||||||
tab2_Lbl_Language=Language
|
tab2_Lbl_Language=Language
|
||||||
windowBodyRestartToApplyLang=Please restart application to apply changes.
|
windowBodyRestartToApplyLang=Please restart application to apply changes.
|
||||||
tab2_Cb_GLshowNspOnly=Show only *.nsp in GoldLeaf.
|
tab2_Cb_GLshowNspOnly=Show only *.nsp in GoldLeaf.
|
||||||
tab2_Cb_UseOldGlVersion=Use old GoldLeaf version
|
tab2_Cb_UseOldGlVersion=Use old GoldLeaf version
|
||||||
|
btn_OpenSplitFile=Select split NSP
|
||||||
|
|
|
@ -38,7 +38,7 @@ windowTitleNewVersionNOTAval=Keine neue Version verf\u00FCgbar
|
||||||
windowTitleNewVersionUnknown=Nicht in der Lage nach Updates zu suchen
|
windowTitleNewVersionUnknown=Nicht in der Lage nach Updates zu suchen
|
||||||
windowBodyNewVersionUnknown=Etwas ist schiefgelaufen\nInternet vielleicht nicht verf\u00FCgbar, oder GitHub nicht verf\u00FCgbar
|
windowBodyNewVersionUnknown=Etwas ist schiefgelaufen\nInternet vielleicht nicht verf\u00FCgbar, oder GitHub nicht verf\u00FCgbar
|
||||||
windowBodyNewVersionNOTAval=Du benutzt die neueste Version
|
windowBodyNewVersionNOTAval=Du benutzt die neueste Version
|
||||||
tab2_Cb_AllowXciNszXcz=Erlaube XCI/NSZ/XCZ-Dateien-Verwendung f\u00FCr Tinfoil
|
tab2_Cb_AllowXciNszXcz=Erlaube XCI- NSZ- XCZ-Dateien-Verwendung f\u00FCr Tinfoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=Von einigen Drittanbietern verwendet, welche XCI/NSZ/XCZ unterst\u00FCtzen, nutzt Tinfoil Transfer Protocol. Nicht \u00E4ndern, wenn unsicher.
|
tab2_Lbl_AllowXciNszXczDesc=Von einigen Drittanbietern verwendet, welche XCI/NSZ/XCZ unterst\u00FCtzen, nutzt Tinfoil Transfer Protocol. Nicht \u00E4ndern, wenn unsicher.
|
||||||
tab2_Lbl_Language=Sprache
|
tab2_Lbl_Language=Sprache
|
||||||
windowBodyRestartToApplyLang=Bitte die Applikation neustarten um die Einstellungen zu \u00FCbernehmen.
|
windowBodyRestartToApplyLang=Bitte die Applikation neustarten um die Einstellungen zu \u00FCbernehmen.
|
||||||
|
|
|
@ -38,7 +38,7 @@ windowTitleNewVersionNOTAval=Aucune nouvelle version disponible
|
||||||
windowTitleNewVersionUnknown=Impossible de v\u00E9rifier les nouvelles versions
|
windowTitleNewVersionUnknown=Impossible de v\u00E9rifier les nouvelles versions
|
||||||
windowBodyNewVersionNOTAval=Vous utilisez la derni\u00E8re version
|
windowBodyNewVersionNOTAval=Vous utilisez la derni\u00E8re version
|
||||||
windowBodyNewVersionUnknown=Une erreur s'est produite\nPeut-\u00EAtre des probl\u00E8mes de connexion Internet ou GitHub est en panne
|
windowBodyNewVersionUnknown=Une erreur s'est produite\nPeut-\u00EAtre des probl\u00E8mes de connexion Internet ou GitHub est en panne
|
||||||
tab2_Cb_AllowXciNszXcz=Autoriser la s\u00E9lection de fichiers XCI/NSZ/XCZ pour TinFoil
|
tab2_Cb_AllowXciNszXcz=Autoriser la s\u00E9lection de fichiers XCI / NSZ / XCZ pour TinFoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=Utilis\u00E9 par certaines applications tierces prenant en charge XCI/NSZ/XCZ et utilisant le protocole de transfert TinFoil. Ne changez pas en cas de doute.
|
tab2_Lbl_AllowXciNszXczDesc=Utilis\u00E9 par certaines applications tierces prenant en charge XCI/NSZ/XCZ et utilisant le protocole de transfert TinFoil. Ne changez pas en cas de doute.
|
||||||
tab2_Lbl_Language=La langue
|
tab2_Lbl_Language=La langue
|
||||||
|
|
||||||
|
|
|
@ -38,6 +38,6 @@ windowTitleNewVersionNOTAval=\uC0C8\uB85C\uC6B4 \uBC84\uC804\uC774 \uC5C6\uC2B5\
|
||||||
windowTitleNewVersionUnknown=\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
windowTitleNewVersionUnknown=\uC0C8 \uBC84\uC804\uC744 \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.
|
||||||
windowBodyNewVersionUnknown=\uBB38\uC81C\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.\n\uC778\uD130\uB137\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uAC70\uB098 GitHub\uC774 \uB2E4\uC6B4\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
|
windowBodyNewVersionUnknown=\uBB38\uC81C\uAC00 \uBC1C\uC0DD\uD588\uC2B5\uB2C8\uB2E4.\n\uC778\uD130\uB137\uC744 \uC0AC\uC6A9\uD560 \uC218 \uC5C6\uAC70\uB098 GitHub\uC774 \uB2E4\uC6B4\uB418\uC5C8\uC744 \uC218 \uC788\uC2B5\uB2C8\uB2E4.
|
||||||
windowBodyNewVersionNOTAval=\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.
|
windowBodyNewVersionNOTAval=\uCD5C\uC2E0 \uBC84\uC804\uC744 \uC0AC\uC6A9 \uC911\uC785\uB2C8\uB2E4.
|
||||||
tab2_Cb_AllowXciNszXcz=TinFoil\uC5D0 \uB300\uD55C XCI/NSZ/XCZ \uD30C\uC77C \uC120\uD0DD \uD5C8\uC6A9
|
tab2_Cb_AllowXciNszXcz=TinFoil\uC5D0 \uB300\uD55C XCI / NSZ / XCZ \uD30C\uC77C \uC120\uD0DD \uD5C8\uC6A9
|
||||||
tab2_Lbl_AllowXciNszXczDesc=XCI/NSZ/XCZ\uB97C \uC9C0\uC6D0\uD558\uACE0 TinFoil \uC804\uC1A1 \uD504\uB85C\uD1A0\uCF5C\uC744 \uC0AC\uC6A9\uD558\uB294 \uC77C\uBD80 \uD0C0\uC0AC \uC751\uC6A9 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C \uC0AC\uC6A9\uB429\uB2C8\uB2E4. \uD655\uC2E4\uD558\uC9C0 \uC54A\uC73C\uBA74 \uBCC0\uACBD\uD558\uC9C0 \uB9C8\uC2ED\uC2DC\uC624.
|
tab2_Lbl_AllowXciNszXczDesc=XCI/NSZ/XCZ\uB97C \uC9C0\uC6D0\uD558\uACE0 TinFoil \uC804\uC1A1 \uD504\uB85C\uD1A0\uCF5C\uC744 \uC0AC\uC6A9\uD558\uB294 \uC77C\uBD80 \uD0C0\uC0AC \uC751\uC6A9 \uD504\uB85C\uADF8\uB7A8\uC5D0\uC11C \uC0AC\uC6A9\uB429\uB2C8\uB2E4. \uD655\uC2E4\uD558\uC9C0 \uC54A\uC73C\uBA74 \uBCC0\uACBD\uD558\uC9C0 \uB9C8\uC2ED\uC2DC\uC624.
|
||||||
tab2_Lbl_Language=\uC5B8\uC5B4
|
tab2_Lbl_Language=\uC5B8\uC5B4
|
|
@ -38,10 +38,11 @@ windowTitleNewVersionNOTAval=\u041D\u0435\u0442 \u043D\u043E\u0432\u044B\u0445 \
|
||||||
windowTitleNewVersionUnknown=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0430\u043B\u0438\u0447\u0438\u0435 \u043D\u043E\u0432\u044B\u0445 \u0432\u0435\u0440\u0441\u0438\u0439
|
windowTitleNewVersionUnknown=\u041D\u0435\u0432\u043E\u0437\u043C\u043E\u0436\u043D\u043E \u043F\u0440\u043E\u0432\u0435\u0440\u0438\u0442\u044C \u043D\u0430\u043B\u0438\u0447\u0438\u0435 \u043D\u043E\u0432\u044B\u0445 \u0432\u0435\u0440\u0441\u0438\u0439
|
||||||
windowBodyNewVersionNOTAval=\u0412\u044B \u0443\u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0435 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044E\u044E \u0432\u0435\u0440\u0441\u0438\u044E
|
windowBodyNewVersionNOTAval=\u0412\u044B \u0443\u0436\u0435 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0435 \u043F\u043E\u0441\u043B\u0435\u0434\u043D\u044E\u044E \u0432\u0435\u0440\u0441\u0438\u044E
|
||||||
windowBodyNewVersionUnknown=\u0427\u0442\u043E-\u0442\u043E \u043F\u043E\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A.\n\u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043D\u0435\u0442 \u0438\u043D\u0442\u0435\u0440\u043D\u0435\u0442\u0430 \u0438\u043B\u0438 GitHub \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D.
|
windowBodyNewVersionUnknown=\u0427\u0442\u043E-\u0442\u043E \u043F\u043E\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A.\n\u041C\u043E\u0436\u0435\u0442 \u0431\u044B\u0442\u044C \u043D\u0435\u0442 \u0438\u043D\u0442\u0435\u0440\u043D\u0435\u0442\u0430 \u0438\u043B\u0438 GitHub \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u0435\u043D.
|
||||||
tab2_Cb_AllowXciNszXcz=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0432\u044B\u0431\u043E\u0440 XCI/NSZ/XCZ \u0444\u0430\u0439\u043B\u043E\u0432 \u0434\u043B\u044F TinFoil
|
tab2_Cb_AllowXciNszXcz=\u0420\u0430\u0437\u0440\u0435\u0448\u0438\u0442\u044C \u0432\u044B\u0431\u043E\u0440 XCI / NSZ / XCZ \u0444\u0430\u0439\u043B\u043E\u0432 \u0434\u043B\u044F TinFoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442 XCI, NSZ, XCZ \u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438 TinFoil. \u041D\u0435 \u043C\u0435\u043D\u044F\u0439\u0442\u0435 \u0435\u0441\u043B\u0438 \u043D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D\u044B.
|
tab2_Lbl_AllowXciNszXczDesc=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u0442\u0441\u044F \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u044F\u043C\u0438, \u043A\u043E\u0442\u043E\u0440\u044B\u0435 \u043F\u043E\u0434\u0434\u0435\u0440\u0436\u0438\u0432\u0430\u044E\u0442 XCI, NSZ, XCZ \u0438 \u0438\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u044E\u0442 \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0438 TinFoil. \u041D\u0435 \u043C\u0435\u043D\u044F\u0439\u0442\u0435 \u0435\u0441\u043B\u0438 \u043D\u0435 \u0443\u0432\u0435\u0440\u0435\u043D\u044B.
|
||||||
tab2_Lbl_Language=\u042F\u0437\u044B\u043A
|
tab2_Lbl_Language=\u042F\u0437\u044B\u043A
|
||||||
windowBodyRestartToApplyLang=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0447\u0442\u043E\u0431\u044B \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u0441\u0442\u0443\u043F\u0438\u043B\u0438 \u0432 \u0441\u0438\u043B\u0443.
|
windowBodyRestartToApplyLang=\u041F\u043E\u0436\u0430\u043B\u0443\u0439\u0441\u0442\u0430, \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0438\u0442\u0435 \u043F\u0440\u0438\u043B\u043E\u0436\u0435\u043D\u0438\u0435 \u0447\u0442\u043E\u0431\u044B \u0438\u0437\u043C\u0435\u043D\u0435\u043D\u0438\u044F \u0432\u0441\u0442\u0443\u043F\u0438\u043B\u0438 \u0432 \u0441\u0438\u043B\u0443.
|
||||||
tab2_Cb_GLshowNspOnly=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0442\u044C \u0438\u0441\u043A\u043B\u044E\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0444\u0430\u0439\u043B\u044B *.nsp \u0432 GoldLeaf.
|
tab2_Cb_GLshowNspOnly=\u041E\u0442\u043E\u0431\u0440\u0430\u0436\u0430\u0442\u044C \u0438\u0441\u043A\u043B\u044E\u0447\u0438\u0442\u0435\u043B\u044C\u043D\u043E \u0444\u0430\u0439\u043B\u044B *.nsp \u0432 GoldLeaf.
|
||||||
tab2_Cb_UseOldGlVersion=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0441\u0442\u0430\u0440\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E GoldLeaf
|
tab2_Cb_UseOldGlVersion=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u043E\u0432\u0430\u0442\u044C \u0441\u0442\u0430\u0440\u0443\u044E \u0432\u0435\u0440\u0441\u0438\u044E GoldLeaf
|
||||||
|
btn_OpenSplitFile=\u0412\u044B\u0431\u0440\u0430\u0442\u044C \u0440\u0430\u0437\u0431\u0438\u0442\u044B\u0439 NSP
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ windowTitleNewVersionNOTAval=No hay actualizaciones disponibles
|
||||||
windowTitleNewVersionUnknown=No fue posible encontrar actualizaciones
|
windowTitleNewVersionUnknown=No fue posible encontrar actualizaciones
|
||||||
windowBodyNewVersionUnknown=Algo fall\u00F3\nLa conexi\u00F3n a internet no funciona correctamente, o GitHub est\u00E1 ca\u00EDdo
|
windowBodyNewVersionUnknown=Algo fall\u00F3\nLa conexi\u00F3n a internet no funciona correctamente, o GitHub est\u00E1 ca\u00EDdo
|
||||||
windowBodyNewVersionNOTAval=Est\u00E1s usando la \u00FAltima versi\u00F3n
|
windowBodyNewVersionNOTAval=Est\u00E1s usando la \u00FAltima versi\u00F3n
|
||||||
tab2_Cb_AllowXciNszXcz=Permite la selecci\u00F3n de archivos XCI/NSZ/XCZ para Tinfoil
|
tab2_Cb_AllowXciNszXcz=Permite la selecci\u00F3n de archivos XCI / NSZ / XCZ para Tinfoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=Usado por algunas aplicaciones de terceros que soportan XCI/NSZ/XCZ y que utilizan el protocolo de transferencia de Tinfoil. Si no est\u00E1 seguro no cambie la opci\u00F3n.
|
tab2_Lbl_AllowXciNszXczDesc=Usado por algunas aplicaciones de terceros que soportan XCI/NSZ/XCZ y que utilizan el protocolo de transferencia de Tinfoil. Si no est\u00E1 seguro no cambie la opci\u00F3n.
|
||||||
tab2_Lbl_Language=Idioma
|
tab2_Lbl_Language=Idioma
|
||||||
windowBodyRestartToApplyLang=Por favor, reinicie el programa para aplicar los cambios.
|
windowBodyRestartToApplyLang=Por favor, reinicie el programa para aplicar los cambios.
|
||||||
|
|
|
@ -38,9 +38,10 @@ windowTitleNewVersionNOTAval=\u041D\u0435\u043C\u0430\u0454 \u043D\u043E\u0432\u
|
||||||
windowTitleNewVersionUnknown=\u041D\u0435\u043C\u043E\u0436\u043B\u0438\u0432\u043E \u043F\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043D\u0430\u044F\u0432\u043D\u0456\u0441\u0442\u044C \u043D\u043E\u0432\u0438\u0445 \u0432\u0435\u0440\u0441\u0456\u0439
|
windowTitleNewVersionUnknown=\u041D\u0435\u043C\u043E\u0436\u043B\u0438\u0432\u043E \u043F\u0435\u0440\u0435\u0432\u0456\u0440\u0438\u0442\u0438 \u043D\u0430\u044F\u0432\u043D\u0456\u0441\u0442\u044C \u043D\u043E\u0432\u0438\u0445 \u0432\u0435\u0440\u0441\u0456\u0439
|
||||||
windowBodyNewVersionNOTAval=\u0412\u0438 \u0432\u0436\u0435 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454\u0442\u0435 \u043E\u0441\u0442\u0430\u043D\u043D\u044E \u0432\u0435\u0440\u0441\u0456\u044E
|
windowBodyNewVersionNOTAval=\u0412\u0438 \u0432\u0436\u0435 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454\u0442\u0435 \u043E\u0441\u0442\u0430\u043D\u043D\u044E \u0432\u0435\u0440\u0441\u0456\u044E
|
||||||
windowBodyNewVersionUnknown=\u0429\u043E\u0441\u044C \u043F\u0456\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A.\n\u041C\u043E\u0436\u043B\u0438\u0432\u043E, \u0456\u043D\u0442\u0435\u0440\u043D\u0435\u0442 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0438\u0439, \u0430\u0431\u043E GitHub \u043D\u0435 \u043F\u0440\u0430\u0446\u044E\u0454.
|
windowBodyNewVersionUnknown=\u0429\u043E\u0441\u044C \u043F\u0456\u0448\u043B\u043E \u043D\u0435 \u0442\u0430\u043A.\n\u041C\u043E\u0436\u043B\u0438\u0432\u043E, \u0456\u043D\u0442\u0435\u0440\u043D\u0435\u0442 \u043D\u0435\u0434\u043E\u0441\u0442\u0443\u043F\u043D\u0438\u0439, \u0430\u0431\u043E GitHub \u043D\u0435 \u043F\u0440\u0430\u0446\u044E\u0454.
|
||||||
tab2_Cb_AllowXciNszXcz=\u0414\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 \u0432\u0438\u0431\u0456\u0440 XCI/NSZ/XCZ \u0444\u0430\u0439\u043B\u0456\u0432 \u0434\u043B\u044F \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u0430\u043D\u043D\u044F \u0443 TinFoil
|
tab2_Cb_AllowXciNszXcz=\u0414\u043E\u0437\u0432\u043E\u043B\u0438\u0442\u0438 \u0432\u0438\u0431\u0456\u0440 XCI / NSZ / XCZ \u0444\u0430\u0439\u043B\u0456\u0432 \u0434\u043B\u044F \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u0430\u043D\u043D\u044F \u0443 TinFoil
|
||||||
tab2_Lbl_AllowXciNszXczDesc=\u0412\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454\u0442\u044C\u0441\u044F \u0434\u043E\u0434\u0430\u0442\u043A\u0430\u043C\u0438, \u0449\u043E \u043C\u0430\u044E\u0442\u044C \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u043A\u0443 XCI, NSZ, XCZ \u0456 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u044E\u0442\u044C \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0456 TinFoil. \u041D\u0435 \u0437\u043C\u0456\u043D\u044E\u0439\u0442\u0435, \u044F\u043A\u0449\u043E \u043D\u0435 \u0432\u043F\u0435\u0432\u043D\u0435\u043D\u0456.
|
tab2_Lbl_AllowXciNszXczDesc=\u0412\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0454\u0442\u044C\u0441\u044F \u0434\u043E\u0434\u0430\u0442\u043A\u0430\u043C\u0438, \u0449\u043E \u043C\u0430\u044E\u0442\u044C \u043F\u0456\u0434\u0442\u0440\u0438\u043C\u043A\u0443 XCI, NSZ, XCZ \u0456 \u0432\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u044E\u0442\u044C \u043F\u0440\u043E\u0442\u043E\u043A\u043E\u043B \u043F\u0435\u0440\u0435\u0434\u0430\u0447\u0456 TinFoil. \u041D\u0435 \u0437\u043C\u0456\u043D\u044E\u0439\u0442\u0435, \u044F\u043A\u0449\u043E \u043D\u0435 \u0432\u043F\u0435\u0432\u043D\u0435\u043D\u0456.
|
||||||
tab2_Lbl_Language=\u041C\u043E\u0432\u0430
|
tab2_Lbl_Language=\u041C\u043E\u0432\u0430
|
||||||
windowBodyRestartToApplyLang=\u0411\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0456\u0442\u044C \u0434\u043E\u0434\u0430\u0442\u043E\u043A \u0449\u043E\u0431 \u0437\u043C\u0456\u043D\u0438 \u0432\u0441\u0442\u0443\u043F\u0438\u043B\u0438 \u0432 \u0441\u0438\u043B\u0443.
|
windowBodyRestartToApplyLang=\u0411\u0443\u0434\u044C \u043B\u0430\u0441\u043A\u0430, \u043F\u0435\u0440\u0435\u0437\u0430\u043F\u0443\u0441\u0442\u0456\u0442\u044C \u0434\u043E\u0434\u0430\u0442\u043E\u043A \u0449\u043E\u0431 \u0437\u043C\u0456\u043D\u0438 \u0432\u0441\u0442\u0443\u043F\u0438\u043B\u0438 \u0432 \u0441\u0438\u043B\u0443.
|
||||||
tab2_Cb_GLshowNspOnly=\u0412\u0456\u0434\u043E\u0431\u0440\u0430\u0436\u0430\u0442\u0438 \u0432\u0438\u043A\u043B\u044E\u0447\u043D\u043E *.nsp \u0444\u0430\u0439\u043B\u0438 \u0443 GoldLeaf.
|
tab2_Cb_GLshowNspOnly=\u0412\u0456\u0434\u043E\u0431\u0440\u0430\u0436\u0430\u0442\u0438 \u0432\u0438\u043A\u043B\u044E\u0447\u043D\u043E *.nsp \u0444\u0430\u0439\u043B\u0438 \u0443 GoldLeaf.
|
||||||
tab2_Cb_UseOldGlVersion=\u0412\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0432\u0430\u0442\u0438 \u0441\u0442\u0430\u0440\u0443 \u0432\u0435\u0440\u0441\u0456\u044E GoldLeaf
|
tab2_Cb_UseOldGlVersion=\u0412\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u0432\u0443\u0432\u0430\u0442\u0438 \u0441\u0442\u0430\u0440\u0443 \u0432\u0435\u0440\u0441\u0456\u044E GoldLeaf
|
||||||
|
btn_OpenSplitFile=\u0412\u0438\u0431\u0440\u0430\u0442\u0438 \u0440\u043E\u0437\u0431\u0438\u0442\u0438\u0439 NSP
|
Loading…
Reference in a new issue