From 049c07fe8d3bda533a74a72efae1ab78d0386e65 Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Sun, 27 Oct 2019 00:02:40 +0300 Subject: [PATCH] Add split-files support for every 'protocol' supported --- README.md | 11 +- pom.xml | 2 +- .../COM/Helpers/NSSplitReader.java | 91 +++++++++ .../{ => COM}/NET/NETCommunications.java | 177 +++++++++++++----- .../nsusbloader/{ => COM}/NET/NETPacket.java | 2 +- .../nsusbloader/{ => COM}/USB/GoldLeaf.java | 156 ++++++++++----- .../{ => COM}/USB/GoldLeaf_05.java | 83 +++++--- .../{ => COM}/USB/PFS/NCAFile.java | 2 +- .../{ => COM}/USB/PFS/PFSProvider.java | 10 +- .../nsusbloader/{ => COM}/USB/TinFoil.java | 128 ++++++++----- .../nsusbloader/COM/USB/TransferModule.java | 54 ++++++ .../{ => COM}/USB/UsbCommunications.java | 4 +- .../nsusbloader/{ => COM}/USB/UsbConnect.java | 2 +- .../{ => COM}/USB/UsbErrorCodes.java | 2 +- .../Controllers/NSLMainController.java | 95 +++++----- .../nsusbloader/Controllers/NSLRowModel.java | 11 +- .../Controllers/NSTableViewController.java | 18 ++ src/main/java/nsusbloader/NSLMain.java | 2 +- src/main/java/nsusbloader/RainbowHexDump.java | 1 + .../java/nsusbloader/USB/ITransferModule.java | 7 - src/main/resources/NSLMain.fxml | 6 +- src/main/resources/locale.properties | 3 +- src/main/resources/locale_deu.properties | 2 +- src/main/resources/locale_fra.properties | 2 +- src/main/resources/locale_kor.properties | 2 +- src/main/resources/locale_rus.properties | 3 +- src/main/resources/locale_spa.properties | 2 +- src/main/resources/locale_ukr.properties | 5 +- 28 files changed, 618 insertions(+), 265 deletions(-) create mode 100644 src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java rename src/main/java/nsusbloader/{ => COM}/NET/NETCommunications.java (71%) rename src/main/java/nsusbloader/{ => COM}/NET/NETPacket.java (99%) rename src/main/java/nsusbloader/{ => COM}/USB/GoldLeaf.java (90%) rename src/main/java/nsusbloader/{ => COM}/USB/GoldLeaf_05.java (83%) rename src/main/java/nsusbloader/{ => COM}/USB/PFS/NCAFile.java (96%) rename src/main/java/nsusbloader/{ => COM}/USB/PFS/PFSProvider.java (96%) rename src/main/java/nsusbloader/{ => COM}/USB/TinFoil.java (74%) create mode 100644 src/main/java/nsusbloader/COM/USB/TransferModule.java rename src/main/java/nsusbloader/{ => COM}/USB/UsbCommunications.java (98%) rename src/main/java/nsusbloader/{ => COM}/USB/UsbConnect.java (99%) rename src/main/java/nsusbloader/{ => COM}/USB/UsbErrorCodes.java (97%) delete mode 100644 src/main/java/nsusbloader/USB/ITransferModule.java diff --git a/README.md b/README.md index eebb35c..789a1a5 100644 --- a/README.md +++ b/README.md @@ -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). -![Screenshot](https://farm8.staticflickr.com/7809/46703921964_53f60f04ed_o.png) +![Screenshot](https://live.staticflickr.com/65535/48962978677_4c3913e8a9_o.png) #### 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). -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. @@ -140,13 +140,16 @@ usb4java since NS-USBloader-v0.2.3 switched to 1.2.0 instead of 1.3.0. This shou ### 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. -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): - [x] [Android support](https://github.com/developersu/ns-usbloader-mobile) - [ ] File order sort (non-critical) - [ ] More deep file analyze before uploading. -- [ ] XCI support ## Support this app diff --git a/pom.xml b/pom.xml index e8b13d9..3ba7614 100644 --- a/pom.xml +++ b/pom.xml @@ -8,7 +8,7 @@ NS-USBloader ns-usbloader - 0.8.2-SNAPSHOT + 0.9-SNAPSHOT https://github.com/developersu/ns-usbloader/ diff --git a/src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java b/src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java new file mode 100644 index 0000000..14fec9b --- /dev/null +++ b/src/main/java/nsusbloader/COM/Helpers/NSSplitReader.java @@ -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(); + } +} diff --git a/src/main/java/nsusbloader/NET/NETCommunications.java b/src/main/java/nsusbloader/COM/NET/NETCommunications.java similarity index 71% rename from src/main/java/nsusbloader/NET/NETCommunications.java rename to src/main/java/nsusbloader/COM/NET/NETCommunications.java index 0a42575..d17aea6 100644 --- a/src/main/java/nsusbloader/NET/NETCommunications.java +++ b/src/main/java/nsusbloader/COM/NET/NETCommunications.java @@ -1,9 +1,10 @@ -package nsusbloader.NET; +package nsusbloader.COM.NET; import javafx.concurrent.Task; import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.COM.Helpers.NSSplitReader; import java.io.*; import java.net.*; @@ -22,6 +23,7 @@ public class NETCommunications extends Task { // todo: thows IOException? private String switchIP; private HashMap nspMap; + private HashMap nspFileSizes; private ServerSocket serverSocket; @@ -42,6 +44,29 @@ public class NETCommunications extends Task { // todo: thows IOException? this.switchIP = switchIP; this.logPrinter = new LogPrinter(); 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 try { for (File nspFile : filesList) @@ -49,12 +74,27 @@ public class NETCommunications extends Task { // todo: thows IOException? } catch (UnsupportedEncodingException uee){ isValid = false; - 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) - nspMap.put(nspFile.getName(), nspFile); + 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) + // nspMap.put(nspFile.getName(), nspFile); close(EFileStatus.FAILED); return; } + // Collect sizes since now we can have split-files support + for (Map.Entry 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 if (hostIPaddr.isEmpty()) { DatagramSocket socket; @@ -242,23 +282,23 @@ public class NETCommunications extends Task { // todo: thows IOException? LinkedList tcpPacket = new LinkedList<>(); while ((line = br.readLine()) != null) { - //System.out.println(line); // TODO: remove DBG - if (line.trim().isEmpty()) { // If TCP packet is ended + //System.out.println(line); // Debug + if (line.trim().isEmpty()) { // If TCP packet is ended if (handleRequest(tcpPacket)) // Proceed required things break work_routine; tcpPacket.clear(); // Clear data and wait for next TCP packet } else - tcpPacket.add(line); // Otherwise collect data + tcpPacket.add(line); // Otherwise collect data } // and reopen client sock 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; } } - if (!isCancelled()) + if ( ! isCancelled() ) close(EFileStatus.UNKNOWN); return null; } @@ -271,8 +311,11 @@ public class NETCommunications extends Task { // todo: thows IOException? private boolean handleRequest(LinkedList packet){ //private boolean handleRequest(LinkedList packet, OutputStreamWriter pw){ File requestedFile; - requestedFile = nspMap.get(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 + String reqFileName = packet.get(0).replaceAll("(^[A-z\\s]+/)|(\\s+?.*$)", ""); + 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.flush(); 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 { // todo: thows IOException? return true; } if (packet.get(0).startsWith("HEAD")){ - currSockPW.write(NETPacket.getCode200(requestedFile.length())); + currSockPW.write(NETPacket.getCode200(reqFileSize)); currSockPW.flush(); logPrinter.print("NET: Replying for requested file: "+requestedFile.getName(), EMsgType.INFO); return false; @@ -298,23 +341,23 @@ public class NETCommunications extends Task { // todo: thows IOException? logPrinter.update(requestedFile, EFileStatus.FAILED); 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; } 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; } else if (!rangeStr[1].isEmpty()) { // If only END defined: Try to read last 500 bytes - if (requestedFile.length() > 500){ - if (writeToSocket(requestedFile, requestedFile.length()-500, requestedFile.length())) // DO WRITE + if (reqFileSize > 500){ + if (writeToSocket(reqFileName, reqFileSize-500, reqFileSize)) // DO WRITE return true; } else { // If file smaller than 500 bytes currSockPW.write(NETPacket.getCode416()); 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); return true; } @@ -343,50 +386,84 @@ public class NETCommunications extends Task { // todo: thows IOException? /** * 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); - currSockPW.write(NETPacket.getCode206(file.length(), start, end)); + // Reply + currSockPW.write(NETPacket.getCode206(nspFileSizes.get(fileName), start, end)); currSockPW.flush(); + // Prepare transfer + long count = end - start + 1; + + int readPice = 8388608; // = 8Mb + byte[] byteBuf; + long currentOffset = 0; + try{ - long count = end - start + 1; + //================================= SPLIT FILE ==================================== + if (reqFile.isDirectory()){ + NSSplitReader nsr = new NSSplitReader(reqFile, start); - BufferedInputStream bis = new BufferedInputStream(new FileInputStream(file)); - int readPice = 8388608; // = 8Mb - byte[] byteBuf; + while (currentOffset < count){ + if (isCancelled()) + return true; + if ((currentOffset + readPice) >= count){ + readPice = Math.toIntExact(count - currentOffset); + } + byteBuf = new byte[readPice]; - if (bis.skip(start) != start){ - logPrinter.print("NET: Unable to skip requested range.", EMsgType.FAIL); - logPrinter.update(file, EFileStatus.FAILED); - return true; - } - long currentOffset = 0; - while (currentOffset < count){ - if (isCancelled()) - return true; - if ((currentOffset+readPice) >= count){ - readPice = Math.toIntExact(count - currentOffset); + if (nsr.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; } - 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){ - logPrinter.print("NET: Reading of file stream suddenly ended.", EMsgType.FAIL); + if (bis.skip(start) != start){ + logPrinter.print("NET: Unable to skip requested range.", EMsgType.FAIL); + logPrinter.update(reqFile, EFileStatus.FAILED); return true; } - currSockOS.write(byteBuf); - //-----------------------------------------/ - logPrinter.updateProgress((currentOffset+readPice)/(count/100.0) / 100.0); - //-----------------------------------------/ - currentOffset += readPice; + + while (currentOffset < count){ + if (isCancelled()) + return true; + 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); - //-----------------------------------------/ + //-------/ } - catch (IOException ioe){ - logPrinter.print("NET: File transmission failed. Returned:\n\t"+ioe.getMessage(), EMsgType.FAIL); - logPrinter.update(file, EFileStatus.FAILED); + catch (IOException | NullPointerException e){ + logPrinter.print("NET: File transmission failed. Returned:\n\t"+e.getMessage(), EMsgType.FAIL); + logPrinter.update(reqFile, EFileStatus.FAILED); return true; } return false; @@ -403,7 +480,7 @@ public class NETCommunications extends Task { // todo: thows IOException? 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); } if (status != null) { diff --git a/src/main/java/nsusbloader/NET/NETPacket.java b/src/main/java/nsusbloader/COM/NET/NETPacket.java similarity index 99% rename from src/main/java/nsusbloader/NET/NETPacket.java rename to src/main/java/nsusbloader/COM/NET/NETPacket.java index f4ca5b7..f7d6807 100644 --- a/src/main/java/nsusbloader/NET/NETPacket.java +++ b/src/main/java/nsusbloader/COM/NET/NETPacket.java @@ -1,4 +1,4 @@ -package nsusbloader.NET; +package nsusbloader.COM.NET; import nsusbloader.NSLMain; diff --git a/src/main/java/nsusbloader/USB/GoldLeaf.java b/src/main/java/nsusbloader/COM/USB/GoldLeaf.java similarity index 90% rename from src/main/java/nsusbloader/USB/GoldLeaf.java rename to src/main/java/nsusbloader/COM/USB/GoldLeaf.java index d395c41..4afba33 100644 --- a/src/main/java/nsusbloader/USB/GoldLeaf.java +++ b/src/main/java/nsusbloader/COM/USB/GoldLeaf.java @@ -1,12 +1,12 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import javafx.application.Platform; import javafx.concurrent.Task; import javafx.stage.FileChooser; import nsusbloader.MediatorControl; import nsusbloader.ModelControllers.LogPrinter; -import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.COM.Helpers.NSSplitReader; import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; @@ -19,14 +19,10 @@ import java.util.*; import java.util.concurrent.CompletableFuture; /** - * GoldLeaf processing + * GoldLeaf 0.7 - 0.7.3 processing */ -class GoldLeaf implements ITransferModule { - private LogPrinter logPrinter; - private DeviceHandle handlerNS; - private LinkedHashMap nspMap; +class GoldLeaf extends TransferModule { private boolean nspFilterForGl; - private Task task; // CMD 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 RandomAccessFile randAccessFile; + private NSSplitReader splitReader; private HashMap writeFilesMap; + private long virtDriveSize; + private HashMap splitFileSize; private boolean isWindows; private String homePath; @@ -53,6 +52,8 @@ class GoldLeaf implements ITransferModule { private File selectedFile; GoldLeaf(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter, boolean nspFilter){ + super(handler, nspMap, task, logPrinter); + final byte CMD_GetDriveCount = 0x00; 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] @@ -72,11 +73,7 @@ class GoldLeaf implements ITransferModule { final byte[] CMD_GLCI = new byte[]{0x47, 0x4c, 0x43, 0x49}; - this.handlerNS = handler; - this.nspMap = nspMap; - this.logPrinter = logPrinter; this.nspFilterForGl = nspFilter; - this.task = task; logPrinter.print("============= GoldLeaf =============\n\tVIRT:/ equals files added into the application\n\tHOME:/ equals " +System.getProperty("user.home"), EMsgType.INFO); @@ -92,6 +89,22 @@ class GoldLeaf implements ITransferModule { 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 byte[] readByte; int someLength1, @@ -193,7 +206,7 @@ class GoldLeaf implements ITransferModule { for (BufferedOutputStream fBufOutStream: writeFilesMap.values()){ try{ fBufOutStream.close(); - }catch (IOException ignored){} + }catch (IOException | NullPointerException ignored){} } } closeOpenedReadFilesGl(); @@ -207,9 +220,14 @@ class GoldLeaf implements ITransferModule { try{ randAccessFile.close(); } - catch (IOException ignored){} + catch (IOException | NullPointerException ignored){} + try{ + splitReader.close(); + } + catch (IOException | NullPointerException ignored){} openReadFileNameAndPath = null; randAccessFile = null; + splitReader = null; } } /** @@ -243,7 +261,7 @@ class GoldLeaf implements ITransferModule { driveLetterLen, totalFreeSpace, totalSize; - long totalSizeLong = 0; + long totalSizeLong; // 0 == VIRTUAL DRIVE if (driveNo == 0){ @@ -252,9 +270,7 @@ class GoldLeaf implements ITransferModule { driveLetter = "VIRT".getBytes(StandardCharsets.UTF_16LE); // TODO: Consider moving to class field declaration driveLetterLen = intToArrLE(driveLetter.length / 2);// since GL 0.7 totalFreeSpace = new byte[4]; - for (File nspFile : nspMap.values()){ - totalSizeLong += nspFile.length(); - } + totalSizeLong = virtDriveSize; totalSize = Arrays.copyOfRange(longToArrLE(totalSizeLong), 0, 4); // Dirty hack; now for GL! } else { //1 == User home dir @@ -567,7 +583,12 @@ class GoldLeaf implements ITransferModule { filePath = filePath.replaceFirst("VIRT:/", ""); if (nspMap.containsKey(filePath)){ 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)) { logPrinter.print("GL Handle 'StatPath' command.", EMsgType.FAIL); return true; @@ -693,7 +714,7 @@ class GoldLeaf implements ITransferModule { * false if everything is ok * */ 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:/")){ // Let's find out which file requested String fNamePath = nspMap.get(fileName.substring(6)).getAbsolutePath(); // NOTE: 6 = "VIRT:/".length @@ -703,14 +724,25 @@ class GoldLeaf implements ITransferModule { if (openReadFileNameAndPath != null){ try{ randAccessFile.close(); - }catch (IOException ignored){} + }catch (Exception ignored){} + try{ + splitReader.close(); + }catch (Exception ignored){} } // Open what has to be opened 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; } - catch (IOException ioe){ + catch (IOException | NullPointerException ioe){ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage()); } } @@ -724,47 +756,79 @@ class GoldLeaf implements ITransferModule { if (openReadFileNameAndPath != null){ try{ randAccessFile.close(); - }catch (IOException ignored){} + }catch (IOException | NullPointerException ignored){} } // Open what has to be opened try{ randAccessFile = new RandomAccessFile(fileName, "r"); openReadFileNameAndPath = fileName; - }catch (IOException ioe){ + }catch (IOException | NullPointerException ioe){ return writeGL_FAIL("GL Handle 'ReadFile' command\n\t"+ioe.getMessage()); } } } //----------------------- Actual transfer chain ------------------------ try{ - randAccessFile.seek(offset); - byte[] chunk = new byte[(int)size]; // WTF MAN? - // 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; + if (randAccessFile == null){ + splitReader.seek(offset); + byte[] chunk = new byte[(int)size]; // WTF MAN? + // Let's find out how much bytes we got + int bytesRead = splitReader.read(chunk); + // Let's check that we read expected size + if (bytesRead != (int)size) + return writeGL_FAIL("GL Handle 'ReadFile' command [CMD]\n" + + " At offset: "+offset+"\n Requested: "+size+"\n Received: "+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; } - // Let's bypass bytes we read total - if (writeToUsb(chunk)) { - logPrinter.print("GL Handle 'ReadFile' command", EMsgType.FAIL); - return true; + else { + randAccessFile.seek(offset); + byte[] chunk = new byte[(int)size]; // WTF MAN? + // 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{ randAccessFile.close(); } - catch (IOException ioee){ - logPrinter.print("GL Handle 'ReadFile' command: unable to close: "+openReadFileNameAndPath+"\n\t"+ioee.getMessage(), EMsgType.WARNING); + catch (NullPointerException ignored){} + 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; randAccessFile = null; + splitReader = null; 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"); } - @Override - public EFileStatus getStatus() { - return EFileStatus.UNKNOWN; - } /*----------------------------------------------------*/ /* GL HELPERS */ /*----------------------------------------------------*/ diff --git a/src/main/java/nsusbloader/USB/GoldLeaf_05.java b/src/main/java/nsusbloader/COM/USB/GoldLeaf_05.java similarity index 83% rename from src/main/java/nsusbloader/USB/GoldLeaf_05.java rename to src/main/java/nsusbloader/COM/USB/GoldLeaf_05.java index 3592051..55aff31 100644 --- a/src/main/java/nsusbloader/USB/GoldLeaf_05.java +++ b/src/main/java/nsusbloader/COM/USB/GoldLeaf_05.java @@ -1,10 +1,11 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import javafx.concurrent.Task; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EFileStatus; 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.LibUsb; @@ -18,7 +19,7 @@ import java.util.LinkedHashMap; /** * GoldLeaf processing * */ -public class GoldLeaf_05 implements ITransferModule{ +public class GoldLeaf_05 extends TransferModule { // CMD G L U C 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 @@ -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_Finish = new byte[]{0x07, 0x00, 0x00, 0x00}; - private DeviceHandle handlerNS; - private Task task; - private LogPrinter logPrinter; - private EFileStatus status = EFileStatus.FAILED; private RandomAccessFile raf; // NSP File + private NSSplitReader nsr; // It'a also NSP File GoldLeaf_05(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter){ + super(handler, nspMap, task, logPrinter); + status = EFileStatus.FAILED; + 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); 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); - this.handlerNS = handler; - this.task = task; - this.logPrinter = logPrinter; 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){ - logPrinter.print("GL File not found\n\t"+fnfe.getMessage(), EMsgType.FAIL); + catch (IOException ioe){ + logPrinter.print("GL File not found\n\t"+ioe.getMessage(), EMsgType.FAIL); return; } @@ -131,9 +132,11 @@ public class GoldLeaf_05 implements ITransferModule{ try { raf.close(); } - catch (IOException ioe){ - logPrinter.print("GL Failed to close file.", EMsgType.INFO); + catch (IOException | NullPointerException ignored){} + try { + nsr.close(); } + catch (IOException | NullPointerException ignored){} } /** * ConnectionResponse command handler @@ -254,21 +257,41 @@ public class GoldLeaf_05 implements ITransferModule{ byte[] readBuf; try{ - raf.seek(realNcaOffset); + if (raf == null){ + nsr.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; + while (readFrom < realNcaSize){ + if (realNcaSize - readFrom < readPice) + readPice = Math.toIntExact(realNcaSize - readFrom); // it's safe, I guarantee + readBuf = new byte[readPice]; + if (nsr.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; + } + } + 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); @@ -282,8 +305,6 @@ public class GoldLeaf_05 implements ITransferModule{ return false; } - @Override - public EFileStatus getStatus() { return status; } /** * Sending any byte array to USB device diff --git a/src/main/java/nsusbloader/USB/PFS/NCAFile.java b/src/main/java/nsusbloader/COM/USB/PFS/NCAFile.java similarity index 96% rename from src/main/java/nsusbloader/USB/PFS/NCAFile.java rename to src/main/java/nsusbloader/COM/USB/PFS/NCAFile.java index 368c79d..6e7dd35 100644 --- a/src/main/java/nsusbloader/USB/PFS/NCAFile.java +++ b/src/main/java/nsusbloader/COM/USB/PFS/NCAFile.java @@ -1,4 +1,4 @@ -package nsusbloader.USB.PFS; +package nsusbloader.COM.USB.PFS; import java.nio.ByteBuffer; import java.nio.ByteOrder; diff --git a/src/main/java/nsusbloader/USB/PFS/PFSProvider.java b/src/main/java/nsusbloader/COM/USB/PFS/PFSProvider.java similarity index 96% rename from src/main/java/nsusbloader/USB/PFS/PFSProvider.java rename to src/main/java/nsusbloader/COM/USB/PFS/PFSProvider.java index 1ca2791..e78a18f 100644 --- a/src/main/java/nsusbloader/USB/PFS/PFSProvider.java +++ b/src/main/java/nsusbloader/COM/USB/PFS/PFSProvider.java @@ -1,8 +1,7 @@ -package nsusbloader.USB.PFS; +package nsusbloader.COM.USB.PFS; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EMsgType; -import nsusbloader.ServiceWindow; import java.io.*; import java.nio.ByteBuffer; @@ -22,9 +21,14 @@ public class PFSProvider { private int ticketID = -1; 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"); - nspFileName = nspFile.getName(); int filesCount; int header; diff --git a/src/main/java/nsusbloader/USB/TinFoil.java b/src/main/java/nsusbloader/COM/USB/TinFoil.java similarity index 74% rename from src/main/java/nsusbloader/USB/TinFoil.java rename to src/main/java/nsusbloader/COM/USB/TinFoil.java index 4a73da9..e7a1db4 100644 --- a/src/main/java/nsusbloader/USB/TinFoil.java +++ b/src/main/java/nsusbloader/COM/USB/TinFoil.java @@ -1,9 +1,10 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import javafx.concurrent.Task; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EFileStatus; import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.COM.Helpers.NSSplitReader; import org.usb4java.DeviceHandle; import org.usb4java.LibUsb; @@ -18,18 +19,9 @@ import java.util.LinkedHashMap; /** * Tinfoil processing * */ -class TinFoil implements ITransferModule { - private LogPrinter logPrinter; - private DeviceHandle handlerNS; - private LinkedHashMap nspMap; - private EFileStatus status = EFileStatus.FAILED; - private Task task; - +class TinFoil extends TransferModule { TinFoil(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter){ - this.handlerNS = handler; - this.nspMap = nspMap; - this.task = task; - this.logPrinter = logPrinter; + super(handler, nspMap, task, logPrinter); logPrinter.print("============= TinFoil =============", EMsgType.INFO); @@ -162,44 +154,83 @@ class TinFoil implements ITransferModule { return false; try { - - 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; - } + byte[] bufferCurrent; //= new byte[1048576]; // eq. Allocate 1mb long currentOffset = 0; // 'End Offset' equal to receivedRangeSize. int readPice = 8388608; // = 8Mb - 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 - //-----------------------------------------/ - logPrinter.updateProgress((currentOffset+readPice)/(receivedRangeSize/100.0) / 100.0); - //-----------------------------------------/ - bufferCurrent = new byte[readPice]; // TODO: not perfect moment, consider refactoring. + //---------------! Split files start !--------------- + if (nspMap.get(receivedRequestedNSP).isDirectory()){ + NSSplitReader nsSplitReader = new NSSplitReader(nspMap.get(receivedRequestedNSP), receivedRangeSize); + if (nsSplitReader.seek(receivedRangeOffset) != receivedRangeOffset){ + logPrinter.print("TF Requested skip is out of file size. Nothing to transmit.", EMsgType.FAIL); + return false; + } - if (bufferedInStream.read(bufferCurrent) != readPice) { // changed since @ v0.3.2 - logPrinter.print("TF Reading of stream suddenly ended.", EMsgType.WARNING); - 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 (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 - if (writeUsb(bufferCurrent)) { - logPrinter.print("TF Failure during NSP transmission.", EMsgType.FAIL); - return false; - } - currentOffset += readPice; + nsSplitReader.close(); + //---Tell progress to UI---/ + logPrinter.updateProgress(1.0); + //------------------------/ } - bufferedInStream.close(); - //-----------------------------------------/ - logPrinter.updateProgress(1.0); - //-----------------------------------------/ + //---------------! Split files end !--------------- + //---------------! Regular files start !--------------- + 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){ logPrinter.print("TF FileNotFoundException:\n "+fnfe.getMessage(), EMsgType.FAIL); 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); ae.printStackTrace(); 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; @@ -309,13 +344,4 @@ class TinFoil implements ITransferModule { logPrinter.print("TF Execution interrupted", EMsgType.INFO); return null; } - - /** - * Status getter - * @return status - */ - @Override - public EFileStatus getStatus() { - return status; - } } diff --git a/src/main/java/nsusbloader/COM/USB/TransferModule.java b/src/main/java/nsusbloader/COM/USB/TransferModule.java new file mode 100644 index 0000000..013c561 --- /dev/null +++ b/src/main/java/nsusbloader/COM/USB/TransferModule.java @@ -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 nspMap; + LogPrinter logPrinter; + DeviceHandle handlerNS; + Task task; + + TransferModule(DeviceHandle handler, LinkedHashMap nspMap, Task 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> 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; } +} diff --git a/src/main/java/nsusbloader/USB/UsbCommunications.java b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java similarity index 98% rename from src/main/java/nsusbloader/USB/UsbCommunications.java rename to src/main/java/nsusbloader/COM/USB/UsbCommunications.java index 4206d13..6fa0348 100644 --- a/src/main/java/nsusbloader/USB/UsbCommunications.java +++ b/src/main/java/nsusbloader/COM/USB/UsbCommunications.java @@ -1,4 +1,4 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import javafx.concurrent.Task; import nsusbloader.ModelControllers.LogPrinter; @@ -50,7 +50,7 @@ public class UsbCommunications extends Task { DeviceHandle handler = usbConnect.getHandlerNS(); - ITransferModule module; + TransferModule module; if (protocol.equals("TinFoil")) module = new TinFoil(handler, nspMap, this, logPrinter); diff --git a/src/main/java/nsusbloader/USB/UsbConnect.java b/src/main/java/nsusbloader/COM/USB/UsbConnect.java similarity index 99% rename from src/main/java/nsusbloader/USB/UsbConnect.java rename to src/main/java/nsusbloader/COM/USB/UsbConnect.java index 2e95cf0..9382a7a 100644 --- a/src/main/java/nsusbloader/USB/UsbConnect.java +++ b/src/main/java/nsusbloader/COM/USB/UsbConnect.java @@ -1,4 +1,4 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import nsusbloader.ModelControllers.LogPrinter; import nsusbloader.NSLDataTypes.EMsgType; diff --git a/src/main/java/nsusbloader/USB/UsbErrorCodes.java b/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java similarity index 97% rename from src/main/java/nsusbloader/USB/UsbErrorCodes.java rename to src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java index 3c7ffde..8c8831b 100644 --- a/src/main/java/nsusbloader/USB/UsbErrorCodes.java +++ b/src/main/java/nsusbloader/COM/USB/UsbErrorCodes.java @@ -1,4 +1,4 @@ -package nsusbloader.USB; +package nsusbloader.COM.USB; import org.usb4java.LibUsb; diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index b86f2e0..ce203b1 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -8,15 +8,15 @@ import javafx.scene.control.*; import javafx.scene.input.DragEvent; import javafx.scene.input.TransferMode; import javafx.scene.layout.Region; +import javafx.stage.DirectoryChooser; import javafx.stage.FileChooser; import nsusbloader.*; import nsusbloader.ModelControllers.UpdatesChecker; -import nsusbloader.NET.NETCommunications; -import nsusbloader.USB.UsbCommunications; +import nsusbloader.COM.NET.NETCommunications; +import nsusbloader.COM.USB.UsbCommunications; import java.io.File; import java.net.*; -import java.util.ArrayList; import java.util.LinkedList; import java.util.List; import java.util.ResourceBundle; @@ -28,9 +28,8 @@ public class NSLMainController implements Initializable { @FXML public TextArea logArea; // Accessible from Mediator @FXML - private Button selectNspBtn; - @FXML - private Button uploadStopBtn; + private Button selectNspBtn, selectSplitNspBtn, uploadStopBtn; + private Region btnUpStopImage; @FXML public ProgressBar progressBar; // Accessible from Mediator @@ -62,6 +61,10 @@ public class NSLMainController implements Initializable { else uploadStopBtn.setDisable(false); selectNspBtn.setOnAction(e-> selectFilesBtnAction()); + + selectSplitNspBtn.setOnAction(e-> selectSplitBtnAction()); + selectSplitNspBtn.getStyleClass().add("buttonSelect"); + uploadStopBtn.setOnAction(e-> uploadBtnAction()); selectNspBtn.getStyleClass().add("buttonSelect"); @@ -101,7 +104,6 @@ public class NSLMainController implements Initializable { public ResourceBundle getResourceBundle() { return resourceBundle; } - /** * Provide hostServices to Settings tab * */ @@ -109,7 +111,6 @@ public class NSLMainController implements Initializable { /** * Functionality for selecting NSP button. - * Uses setReady and setNotReady to simplify code readability. * */ private void selectFilesBtnAction(){ List filesList; @@ -138,6 +139,28 @@ public class NSLMainController implements Initializable { 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 * */ @@ -210,6 +233,7 @@ public class NSLMainController implements Initializable { public void notifyTransmissionStarted(boolean isTransmissionStarted){ if (isTransmissionStarted) { selectNspBtn.setDisable(true); + selectSplitNspBtn.setDisable(true); uploadStopBtn.setOnAction(e-> stopBtnAction()); uploadStopBtn.setText(resourceBundle.getString("btn_Stop")); @@ -222,6 +246,7 @@ public class NSLMainController implements Initializable { } else { selectNspBtn.setDisable(false); + selectSplitNspBtn.setDisable(false); uploadStopBtn.setOnAction(e-> uploadBtnAction()); uploadStopBtn.setText(resourceBundle.getString("btn_Upload")); @@ -253,58 +278,22 @@ public class NSLMainController implements Initializable { /** * Drag-n-drop support (drop consumer) * */ - // TODO: DO SOMETHING WITH THIS @FXML private void handleDrop(DragEvent event){ if (MediatorControl.getInstance().getTransferActive()) { event.setDropCompleted(true); return; } - List filesDropped = new ArrayList<>(); - 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); + List filesDropped = event.getDragboard().getFiles(); - } - }// TODO: Somehow improve this double-action function in settings. - else if (FrontTabController.getSelectedProtocol().equals("GoldLeaf") && (! SettingsTabController.getNSPFileFilterForGL())){ - for (File fileOrDir : event.getDragboard().getFiles()) { - if (fileOrDir.isDirectory()) { - for (File file : fileOrDir.listFiles()) - if ((! file.isDirectory()) && (! file.isHidden())) - filesDropped.add(file); - } - 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()) + if (FrontTabController.getSelectedProtocol().equals("TinFoil") && SettingsTabController.getTfXciNszXczSupport()) + filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches("(.*\\.nsp$)|(.*\\.xci$)|(.*\\.nsz$)|(.*\\.xcz$)")); + else if (FrontTabController.getSelectedProtocol().equals("GoldLeaf") && (! SettingsTabController.getNSPFileFilterForGL())) + filesDropped.removeIf(file -> (file.isDirectory() && ! file.getName().toLowerCase().matches(".*\\.nsp$"))); + else + filesDropped.removeIf(file -> ! file.getName().toLowerCase().matches(".*\\.nsp$")); + + if ( ! filesDropped.isEmpty() ) FrontTabController.tableFilesListController.setFiles(filesDropped); event.setDropCompleted(true); diff --git a/src/main/java/nsusbloader/Controllers/NSLRowModel.java b/src/main/java/nsusbloader/Controllers/NSLRowModel.java index 0e7c9be..1e6866a 100644 --- a/src/main/java/nsusbloader/Controllers/NSLRowModel.java +++ b/src/main/java/nsusbloader/Controllers/NSLRowModel.java @@ -3,6 +3,7 @@ package nsusbloader.Controllers; import nsusbloader.NSLDataTypes.EFileStatus; import java.io.File; +import java.io.FilenameFilter; public class NSLRowModel { @@ -16,7 +17,15 @@ public class NSLRowModel { this.nspFile = nspFile; this.markForUpload = checkBoxValue; 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 = ""; } // Model methods start diff --git a/src/main/java/nsusbloader/Controllers/NSTableViewController.java b/src/main/java/nsusbloader/Controllers/NSTableViewController.java index e628f55..eda3b14 100644 --- a/src/main/java/nsusbloader/Controllers/NSTableViewController.java +++ b/src/main/java/nsusbloader/Controllers/NSTableViewController.java @@ -149,6 +149,24 @@ public class NSTableViewController implements Initializable { table.getColumns().add(fileSizeColumn); table.getColumns().add(uploadColumn); } + /** + * Add single file when user selected it (Split file usually) + * */ + public void setFile(File file){ + if ( ! rowsObsLst.isEmpty()){ + List 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 * */ diff --git a/src/main/java/nsusbloader/NSLMain.java b/src/main/java/nsusbloader/NSLMain.java index c932841..088a2af 100644 --- a/src/main/java/nsusbloader/NSLMain.java +++ b/src/main/java/nsusbloader/NSLMain.java @@ -12,7 +12,7 @@ import java.util.Locale; import java.util.ResourceBundle; public class NSLMain extends Application { - public static final String appVersion = "v0.8.2"; + public static final String appVersion = "v0.9"; @Override public void start(Stage primaryStage) throws Exception{ FXMLLoader loader = new FXMLLoader(getClass().getResource("/NSLMain.fxml")); diff --git a/src/main/java/nsusbloader/RainbowHexDump.java b/src/main/java/nsusbloader/RainbowHexDump.java index 33b4a15..e21b7cc 100644 --- a/src/main/java/nsusbloader/RainbowHexDump.java +++ b/src/main/java/nsusbloader/RainbowHexDump.java @@ -24,6 +24,7 @@ public class RainbowHexDump { System.out.println(">"+ANSI_RED+byteArray.length+ANSI_RESET); for (byte b: byteArray) System.out.print(String.format("%02x ", b)); + //System.out.println(); System.out.print("\t\t\t" + new String(byteArray, StandardCharsets.UTF_8) + "\n"); diff --git a/src/main/java/nsusbloader/USB/ITransferModule.java b/src/main/java/nsusbloader/USB/ITransferModule.java deleted file mode 100644 index 2b053a7..0000000 --- a/src/main/java/nsusbloader/USB/ITransferModule.java +++ /dev/null @@ -1,7 +0,0 @@ -package nsusbloader.USB; - -import nsusbloader.NSLDataTypes.EFileStatus; - -public interface ITransferModule { - EFileStatus getStatus(); -} diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index 0121130..d50cc42 100644 --- a/src/main/resources/NSLMain.fxml +++ b/src/main/resources/NSLMain.fxml @@ -57,7 +57,7 @@ - + +