From bdb91b0e0a985c214f7064641b9ec4c3ee53702c Mon Sep 17 00:00:00 2001 From: Dmitry Isaenko Date: Thu, 26 Sep 2019 05:15:37 +0300 Subject: [PATCH] v0.8 GoldLeaf v0.5 support. Optional. Updated since v0.5.2. --- README.md | 4 +- src/main/java/nsusbloader/AppPreferences.java | 7 +- .../Controllers/NSLMainController.java | 5 +- .../Controllers/SettingsController.java | 30 +- src/main/java/nsusbloader/USB/GoldLeaf.java | 2 +- .../java/nsusbloader/USB/GoldLeaf_05.java | 352 ++++++++++++++++++ .../java/nsusbloader/USB/PFS/NCAFile.java | 25 ++ .../java/nsusbloader/USB/PFS/PFSProvider.java | 183 +++++++++ .../nsusbloader/USB/UsbCommunications.java | 4 +- src/main/resources/SettingsTab.fxml | 6 + src/main/resources/locale.properties | 1 + src/main/resources/locale_rus.properties | 1 + src/main/resources/locale_ukr.properties | 3 +- 13 files changed, 614 insertions(+), 9 deletions(-) create mode 100644 src/main/java/nsusbloader/USB/GoldLeaf_05.java create mode 100644 src/main/java/nsusbloader/USB/PFS/NCAFile.java create mode 100644 src/main/java/nsusbloader/USB/PFS/PFSProvider.java diff --git a/README.md b/README.md index 4601d3b..f9571ca 100644 --- a/README.md +++ b/README.md @@ -40,9 +40,9 @@ JRE/JDK 8u60 or higher. ### Table of supported GoldLeaf versions | GoldLeaf version | NS-USBloader version | | ---------------- | -------------------- | -| v0.5 | v0.4 - v0.5.2 | +| v0.5 | v0.4 - v0.5.2, v0.8 | | v0.6.1 | v0.6 | -| v0.7 | v0.7 | +| v0.7 | v0.7 - v0.8 | ### Usage ##### Linux: diff --git a/src/main/java/nsusbloader/AppPreferences.java b/src/main/java/nsusbloader/AppPreferences.java index 0e28a13..fc001f4 100644 --- a/src/main/java/nsusbloader/AppPreferences.java +++ b/src/main/java/nsusbloader/AppPreferences.java @@ -26,7 +26,8 @@ public class AppPreferences { String HostExtra, boolean autoCheck4Updates, boolean tinfoilXciSupport, - boolean nspFileFilterForGl + boolean nspFileFilterForGl, + String useOldGlVersion ){ setProtocol(Protocol); setRecent(PreviouslyOpened); @@ -43,6 +44,7 @@ public class AppPreferences { setAutoCheckUpdates(autoCheck4Updates); setTfXCI(tinfoilXciSupport); setNspFileFilterGL(nspFileFilterForGl); + setUseOldGlVersion(useOldGlVersion); } public String getTheme(){ String theme = preferences.get("THEME", "/res/app_dark.css"); // Don't let user to change settings manually @@ -114,4 +116,7 @@ public class AppPreferences { public boolean getNspFileFilterGL(){return preferences.getBoolean("GL_NSP_FILTER", false); } public void setNspFileFilterGL(boolean prop){preferences.putBoolean("GL_NSP_FILTER", prop);} + + public String getUseOldGlVersion(){ return preferences.get("OldGlVersion", ""); } + public void setUseOldGlVersion(String version){ preferences.put("OldGlVersion", version);} } diff --git a/src/main/java/nsusbloader/Controllers/NSLMainController.java b/src/main/java/nsusbloader/Controllers/NSLMainController.java index e1506a3..144719a 100644 --- a/src/main/java/nsusbloader/Controllers/NSLMainController.java +++ b/src/main/java/nsusbloader/Controllers/NSLMainController.java @@ -164,7 +164,7 @@ public class NSLMainController implements Initializable { if (FrontTabController.getSelectedProtocol().equals("GoldLeaf") || ( FrontTabController.getSelectedProtocol().equals("TinFoil") && FrontTabController.getSelectedNetUsb().equals("USB") ) ){ - usbNetCommunications = new UsbCommunications(nspToUpload, FrontTabController.getSelectedProtocol(), SettingsTabController.getNSPFileFilterForGL()); + usbNetCommunications = new UsbCommunications(nspToUpload, FrontTabController.getSelectedProtocol()+SettingsTabController.getGlOldVer(), SettingsTabController.getNSPFileFilterForGL()); workThread = new Thread(usbNetCommunications); workThread.setDaemon(true); workThread.start(); @@ -323,7 +323,8 @@ public class NSLMainController implements Initializable { SettingsTabController.getHostExtra(), SettingsTabController.getAutoCheckForUpdates(), SettingsTabController.getTfXCISupport(), - SettingsTabController.getNSPFileFilterForGL() + SettingsTabController.getNSPFileFilterForGL(), + SettingsTabController.getGlOldVer() ); } } diff --git a/src/main/java/nsusbloader/Controllers/SettingsController.java b/src/main/java/nsusbloader/Controllers/SettingsController.java index 087136c..c8950e9 100644 --- a/src/main/java/nsusbloader/Controllers/SettingsController.java +++ b/src/main/java/nsusbloader/Controllers/SettingsController.java @@ -61,8 +61,16 @@ public class SettingsController implements Initializable { @FXML private ChoiceBox langCB; + @FXML + private CheckBox glOldVerCheck; + + @FXML + private ChoiceBox glOldVerChoice; + private HostServices hs; + private static final String[] oldGlSupportedVersions = {"v0.5"}; + @Override public void initialize(URL url, ResourceBundle resourceBundle) { nspFilesFilterForGLCB.setSelected(AppPreferences.getInstance().getNspFileFilterGL()); @@ -240,7 +248,20 @@ public class SettingsController implements Initializable { ResourceBundle.getBundle("locale", new Locale(langCB.getSelectionModel().getSelectedItem())) .getString("windowBodyRestartToApplyLang")); }); - + // Set supported old versions + glOldVerChoice.getItems().addAll(oldGlSupportedVersions); + String oldVer = AppPreferences.getInstance().getUseOldGlVersion(); // Overhead; Too much validation of consistency + if (Arrays.asList(oldGlSupportedVersions).contains(oldVer)) { + glOldVerChoice.getSelectionModel().select(oldVer); + glOldVerChoice.setDisable(false); + glOldVerCheck.setSelected(true); + } + else { + glOldVerChoice.getSelectionModel().select(0); + glOldVerChoice.setDisable(true); + glOldVerCheck.setSelected(false); + } + glOldVerCheck.setOnAction(e-> glOldVerChoice.setDisable(! glOldVerCheck.isSelected()) ); } public boolean getNSPFileFilterForGL(){return nspFilesFilterForGLCB.isSelected(); } public boolean getExpertModeSelected(){ return expertModeCb.isSelected(); } @@ -262,4 +283,11 @@ public class SettingsController implements Initializable { newVersionLink.setVisible(true); newVersionLink.setText("https://github.com/developersu/ns-usbloader/releases/tag/"+newVer); } + + public String getGlOldVer() { + if (glOldVerCheck.isSelected()) + return glOldVerChoice.getValue(); + else + return ""; + } } \ No newline at end of file diff --git a/src/main/java/nsusbloader/USB/GoldLeaf.java b/src/main/java/nsusbloader/USB/GoldLeaf.java index 21cdff1..d395c41 100644 --- a/src/main/java/nsusbloader/USB/GoldLeaf.java +++ b/src/main/java/nsusbloader/USB/GoldLeaf.java @@ -576,7 +576,7 @@ class GoldLeaf implements ITransferModule { } } else if (filePath.startsWith("SPEC:/")){ - System.out.println(filePath); + //System.out.println(filePath); filePath = filePath.replaceFirst("SPEC:/",""); if (selectedFile.getName().equals(filePath)){ command.add(GL_OBJ_TYPE_FILE); diff --git a/src/main/java/nsusbloader/USB/GoldLeaf_05.java b/src/main/java/nsusbloader/USB/GoldLeaf_05.java new file mode 100644 index 0000000..3592051 --- /dev/null +++ b/src/main/java/nsusbloader/USB/GoldLeaf_05.java @@ -0,0 +1,352 @@ +package nsusbloader.USB; + +import javafx.concurrent.Task; +import nsusbloader.ModelControllers.LogPrinter; +import nsusbloader.NSLDataTypes.EFileStatus; +import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.USB.PFS.PFSProvider; +import org.usb4java.DeviceHandle; +import org.usb4java.LibUsb; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.IntBuffer; +import java.util.Arrays; +import java.util.LinkedHashMap; + +/** + * GoldLeaf processing + * */ +public class GoldLeaf_05 implements ITransferModule{ + // 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 + private static final byte[] CMD_NSPName = new byte[]{0x02, 0x00, 0x00, 0x00}; // Write-only command + private static final byte[] CMD_NSPData = new byte[]{0x04, 0x00, 0x00, 0x00}; // Write-only command + + private static final byte[] CMD_ConnectionResponse = new byte[]{0x01, 0x00, 0x00, 0x00}; + private static final byte[] CMD_Start = new byte[]{0x03, 0x00, 0x00, 0x00}; + private static final byte[] CMD_NSPContent = new byte[]{0x05, 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 DeviceHandle handlerNS; + private Task task; + private LogPrinter logPrinter; + private EFileStatus status = EFileStatus.FAILED; + private RandomAccessFile raf; // NSP File + + GoldLeaf_05(DeviceHandle handler, LinkedHashMap nspMap, Task task, LogPrinter logPrinter){ + 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()){ + logPrinter.print("For using this GoldLeaf version you have to add file to the table and select it for upload", EMsgType.INFO); + return; + } + File nspFile = (File) nspMap.values().toArray()[0]; + logPrinter.print("File for upload: "+nspFile.getAbsolutePath(), EMsgType.INFO); + + if (!nspFile.getName().toLowerCase().endsWith(".nsp")) { + logPrinter.print("GL This file doesn't look like NSP", EMsgType.FAIL); + return; + } + PFSProvider pfsElement; + try{ + pfsElement = new PFSProvider(nspFile, logPrinter); + } + catch (Exception e){ + logPrinter.print("GL File provided has incorrect structure and won't be uploaded\n\t"+e.getMessage(), EMsgType.FAIL); + status = EFileStatus.INCORRECT_FILE_FAILED; + return; + } + 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"); + } + catch (FileNotFoundException fnfe){ + logPrinter.print("GL File not found\n\t"+fnfe.getMessage(), EMsgType.FAIL); + return; + } + + // Go parse commands + byte[] readByte; + + // Go connect to GoldLeaf + if (writeUsb(CMD_GLUC)) { + logPrinter.print("GL Initiating GoldLeaf connection [1/2]", EMsgType.FAIL); + return; + } + logPrinter.print("GL Initiating GoldLeaf connection: [1/2]", EMsgType.PASS); + if (writeUsb(CMD_ConnectionRequest)){ + logPrinter.print("GL Initiating GoldLeaf connection: [2/2]", EMsgType.FAIL); + return; + } + logPrinter.print("GL Initiating GoldLeaf connection: [2/2]", EMsgType.PASS); + + while (true) { + readByte = readUsb(); + if (readByte == null) + return; + + if (Arrays.equals(readByte, CMD_GLUC)) { + if ((readByte = readUsb()) == null) + return; + + if (Arrays.equals(readByte, CMD_ConnectionResponse)) { + if (handleConnectionResponse(pfsElement)) + return; + else + continue; + } + if (Arrays.equals(readByte, CMD_Start)) { + if (handleStart(pfsElement)) + return; + else + continue; + } + if (Arrays.equals(readByte, CMD_NSPContent)) { + if (handleNSPContent(pfsElement, true)) + return; + else + continue; + } + if (Arrays.equals(readByte, CMD_NSPTicket)) { + if (handleNSPContent(pfsElement, false)) + return; + else + continue; + } + if (Arrays.equals(readByte, CMD_Finish)) { + logPrinter.print("GL Closing GoldLeaf connection: Transfer successful.", EMsgType.PASS); + status = EFileStatus.UPLOADED; + break; + } + } + } + try { + raf.close(); + } + catch (IOException ioe){ + logPrinter.print("GL Failed to close file.", EMsgType.INFO); + } + } + /** + * ConnectionResponse command handler + * @return true if failed + * false if no issues + * */ + private boolean handleConnectionResponse(PFSProvider pfsElement){ + logPrinter.print("GL 'ConnectionResponse' command:", EMsgType.INFO); + if (writeUsb(CMD_GLUC)) { + logPrinter.print(" [1/4]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [1/4]", EMsgType.PASS); + if (writeUsb(CMD_NSPName)) { + logPrinter.print(" [2/4]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [2/4]", EMsgType.PASS); + + if (writeUsb(pfsElement.getBytesNspFileNameLength())) { + logPrinter.print(" [3/4]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [3/4]", EMsgType.PASS); + + if (writeUsb(pfsElement.getBytesNspFileName())) { + logPrinter.print(" [4/4]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [4/4]", EMsgType.PASS); + + return false; + } + /** + * Start command handler + * @return true if failed + * false if no issues + * */ + private boolean handleStart(PFSProvider pfsElement){ + logPrinter.print("GL Handle 'Start' command:", EMsgType.INFO); + if (writeUsb(CMD_GLUC)) { + logPrinter.print(" [Prefix]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [Prefix]", EMsgType.PASS); + + if (writeUsb(CMD_NSPData)) { + logPrinter.print(" [Command]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [Command]", EMsgType.PASS); + + if (writeUsb(pfsElement.getBytesCountOfNca())) { + logPrinter.print(" [Sub-files count]", EMsgType.FAIL); + return true; + } + logPrinter.print(" [Sub-files count]", EMsgType.PASS); + + int ncaCount = pfsElement.getIntCountOfNca(); + logPrinter.print(" [Information for "+ncaCount+" sub-files]", EMsgType.INFO); + for (int i = 0; i < ncaCount; i++){ + logPrinter.print("File #"+i, EMsgType.INFO); + if (writeUsb(pfsElement.getNca(i).getNcaFileNameLength())) { + logPrinter.print(" [1/4] Name length", EMsgType.FAIL); + return true; + } + logPrinter.print(" [1/4] Name length", EMsgType.PASS); + + if (writeUsb(pfsElement.getNca(i).getNcaFileName())) { + logPrinter.print(" [2/4] Name", EMsgType.FAIL); + return true; + } + logPrinter.print(" [2/4] Name", EMsgType.PASS); + if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getBodySize()+pfsElement.getNca(i).getNcaOffset()).array())) { // offset. real. + logPrinter.print(" [3/4] Offset", EMsgType.FAIL); + return true; + } + logPrinter.print(" [3/4] Offset", EMsgType.PASS); + if (writeUsb(ByteBuffer.allocate(8).order(ByteOrder.LITTLE_ENDIAN).putLong(pfsElement.getNca(i).getNcaSize()).array())) { // size + logPrinter.print(" [4/4] Size", EMsgType.FAIL); + return true; + } + logPrinter.print(" [4/4] Size", EMsgType.PASS); + } + return false; + } + /** + * NSPContent command handler + * @param isItRawRequest true: just ask NS what's needed + * false: send ticket + * @return true if failed + * false if no issues + * */ + private boolean handleNSPContent(PFSProvider pfsElement, boolean isItRawRequest){ + int requestedNcaID; + + if (isItRawRequest) { + logPrinter.print("GL Handle 'Content' command", EMsgType.INFO); + byte[] readByte = readUsb(); + if (readByte == null || readByte.length != 4) { + logPrinter.print(" [Read requested ID]", EMsgType.FAIL); + return true; + } + requestedNcaID = ByteBuffer.wrap(readByte).order(ByteOrder.LITTLE_ENDIAN).getInt(); + logPrinter.print(" [Read requested ID = "+requestedNcaID+" ]", EMsgType.PASS); + } + else { + requestedNcaID = pfsElement.getNcaTicketID(); + logPrinter.print("GL Handle 'Ticket' command (ID = "+requestedNcaID+" )", EMsgType.INFO); + } + + long realNcaOffset = pfsElement.getNca(requestedNcaID).getNcaOffset()+pfsElement.getBodySize(); + long realNcaSize = pfsElement.getNca(requestedNcaID).getNcaSize(); + + long readFrom = 0; + + int readPice = 8388608; // 8mb + byte[] readBuf; + + try{ + 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); + //-----------------------------------------/ + } + catch (IOException ioe){ + logPrinter.print("GL Failed to read NCA ID "+requestedNcaID+". IO Exception:\n "+ioe.getMessage(), EMsgType.FAIL); + ioe.printStackTrace(); + return true; + } + return false; + } + + @Override + public EFileStatus getStatus() { return status; } + + /** + * Sending any byte array to USB device + * @return 'false' if no issues + * 'true' if errors happened + * */ + private boolean writeUsb(byte[] message){ + ByteBuffer writeBuffer = ByteBuffer.allocateDirect(message.length); //writeBuffer.order() equals BIG_ENDIAN; + writeBuffer.put(message); // Don't do writeBuffer.rewind(); + IntBuffer writeBufTransferred = IntBuffer.allocate(1); + int result; + + while (! task.isCancelled()) { + result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint OUT = 0x01 + + switch (result){ + case LibUsb.SUCCESS: + if (writeBufTransferred.get() == message.length) + return false; + else { + logPrinter.print("GL Data transfer issue [write]\n Requested: "+message.length+"\n Transferred: "+writeBufTransferred.get(), EMsgType.FAIL); + return true; + } + case LibUsb.ERROR_TIMEOUT: + continue; + default: + logPrinter.print("GL Data transfer issue [write]\n Returned: "+ UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + logPrinter.print("GL Execution stopped", EMsgType.FAIL); + return true; + } + } + logPrinter.print("GL Execution interrupted", EMsgType.INFO); + return true; + } + /** + * Reading what USB device responded. + * @return byte array if data read successful + * 'null' if read failed + * */ + private byte[] readUsb(){ + ByteBuffer readBuffer = ByteBuffer.allocateDirect(512); + // We can limit it to 32 bytes, but there is a non-zero chance to got OVERFLOW from libusb. + IntBuffer readBufTransferred = IntBuffer.allocate(1); + + int result; + while (! task.isCancelled()) { + result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 + + switch (result) { + case LibUsb.SUCCESS: + int trans = readBufTransferred.get(); + byte[] receivedBytes = new byte[trans]; + readBuffer.get(receivedBytes); + return receivedBytes; + case LibUsb.ERROR_TIMEOUT: + continue; + default: + logPrinter.print("GL Data transfer issue [read]\n Returned: " + UsbErrorCodes.getErrCode(result), EMsgType.FAIL); + logPrinter.print("GL Execution stopped", EMsgType.FAIL); + return null; + } + } + logPrinter.print("GL Execution interrupted", EMsgType.INFO); + return null; + } +} \ No newline at end of file diff --git a/src/main/java/nsusbloader/USB/PFS/NCAFile.java b/src/main/java/nsusbloader/USB/PFS/NCAFile.java new file mode 100644 index 0000000..368c79d --- /dev/null +++ b/src/main/java/nsusbloader/USB/PFS/NCAFile.java @@ -0,0 +1,25 @@ +package nsusbloader.USB.PFS; + +import java.nio.ByteBuffer; +import java.nio.ByteOrder; + +/** + * Data class to hold NCA, tik, xml etc. meta-information + * */ +public class NCAFile { + //private int ncaNumber; + private byte[] ncaFileName; + private long ncaOffset; + private long ncaSize; + + //public void setNcaNumber(int ncaNumber){ this.ncaNumber = ncaNumber; } + void setNcaFileName(byte[] ncaFileName) { this.ncaFileName = ncaFileName; } + void setNcaOffset(long ncaOffset) { this.ncaOffset = ncaOffset; } + void setNcaSize(long ncaSize) { this.ncaSize = ncaSize; } + + //public int getNcaNumber() {return this.ncaNumber; } + public byte[] getNcaFileName() { return ncaFileName; } + public byte[] getNcaFileNameLength() { return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ncaFileName.length).array(); } + public long getNcaOffset() { return ncaOffset; } + public long getNcaSize() { return ncaSize; } +} diff --git a/src/main/java/nsusbloader/USB/PFS/PFSProvider.java b/src/main/java/nsusbloader/USB/PFS/PFSProvider.java new file mode 100644 index 0000000..1ca2791 --- /dev/null +++ b/src/main/java/nsusbloader/USB/PFS/PFSProvider.java @@ -0,0 +1,183 @@ +package nsusbloader.USB.PFS; + +import nsusbloader.ModelControllers.LogPrinter; +import nsusbloader.NSLDataTypes.EMsgType; +import nsusbloader.ServiceWindow; + +import java.io.*; +import java.nio.ByteBuffer; +import java.nio.ByteOrder; +import java.nio.charset.StandardCharsets; +import java.util.*; + +/** + * Used in GoldLeaf USB protocol + * */ +public class PFSProvider { + private static final byte[] PFS0 = new byte[]{0x50, 0x46, 0x53, 0x30}; // PFS0 + + private String nspFileName; + private NCAFile[] ncaFiles; + private long bodySize; + private int ticketID = -1; + + public PFSProvider(File nspFile, LogPrinter logPrinter) throws Exception{ + + RandomAccessFile randAccessFile = new RandomAccessFile(nspFile, "r"); + nspFileName = nspFile.getName(); + + int filesCount; + int header; + + logPrinter.print("PFS Start NSP file analyze for ["+nspFileName+"]", EMsgType.INFO); + + byte[] fileStartingBytes = new byte[12]; + // Read PFS0, files count, header, padding (4 zero bytes) + if (randAccessFile.read(fileStartingBytes) == 12) + logPrinter.print("PFS Read file starting bytes.", EMsgType.PASS); + else { + logPrinter.print("PFS Read file starting bytes.", EMsgType.FAIL); + randAccessFile.close(); + throw new Exception("Unable to read file starting bytes"); + } + // Check PFS0 + if (Arrays.equals(PFS0, Arrays.copyOfRange(fileStartingBytes, 0, 4))) + logPrinter.print("PFS Read 'PFS0'.", EMsgType.PASS); + else + logPrinter.print("PFS Read 'PFS0': this file looks wired.", EMsgType.WARNING); + // Get files count + filesCount = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 4, 8)).order(ByteOrder.LITTLE_ENDIAN).getInt(); + if (filesCount > 0 ) { + logPrinter.print("PFS Read files count [" + filesCount + "]", EMsgType.PASS); + } + else { + logPrinter.print("PFS Read files count", EMsgType.FAIL); + randAccessFile.close(); + throw new Exception("Unable to read file count"); + } + // Get header + header = ByteBuffer.wrap(Arrays.copyOfRange(fileStartingBytes, 8, 12)).order(ByteOrder.LITTLE_ENDIAN).getInt(); + if (header > 0 ) + logPrinter.print("PFS Read header ["+header+"]", EMsgType.PASS); + else { + logPrinter.print("PFS Read header ", EMsgType.FAIL); + randAccessFile.close(); + throw new Exception("Unable to read header"); + } + //********************************************************************************************* + // Create NCA set + this.ncaFiles = new NCAFile[filesCount]; + // Collect files from NSP + byte[] ncaInfoArr = new byte[24]; // should be unsigned long, but.. java.. u know my pain man + + HashMap ncaNameOffsets = new LinkedHashMap<>(); + + int offset; + long nca_offset; + long nca_size; + long nca_name_offset; + + for (int i=0; i= 0?EMsgType.PASS:EMsgType.WARNING); + logPrinter.print(" NCA size check: "+nca_size, nca_size >= 0?EMsgType.PASS: EMsgType.WARNING); + logPrinter.print(" NCA name offset check: "+nca_name_offset, nca_name_offset >= 0?EMsgType.PASS:EMsgType.WARNING); + + NCAFile ncaFile = new NCAFile(); + ncaFile.setNcaOffset(nca_offset); + ncaFile.setNcaSize(nca_size); + this.ncaFiles[i] = ncaFile; + + ncaNameOffsets.put(i, nca_name_offset); + } + // Final offset + byte[] bufForInt = new byte[4]; + if ((randAccessFile.read(bufForInt) == 4) && (Arrays.equals(bufForInt, new byte[4]))) + logPrinter.print("PFS Final padding check", EMsgType.PASS); + else + logPrinter.print("PFS Final padding check", EMsgType.WARNING); + + // Calculate position including header for body size offset + bodySize = randAccessFile.getFilePointer()+header; + //********************************************************************************************* + // Collect file names from NCAs + logPrinter.print("PFS Collecting file names", EMsgType.INFO); + List ncaFN; // Temporary + byte[] b = new byte[1]; // Temporary + for (int i=0; i(); + randAccessFile.seek(filesCount*24+16+ncaNameOffsets.get(i)); // Files cont * 24(bit for each meta-data) + 4 bytes goes after all of them + 12 bit what were in the beginning + while ((randAccessFile.read(b)) != -1){ + if (b[0] == 0x00) + break; + else + ncaFN.add(b[0]); + } + byte[] exchangeTempArray = new byte[ncaFN.size()]; + for (int j=0; j < ncaFN.size(); j++) + exchangeTempArray[j] = ncaFN.get(j); + // Find and store ticket (.tik) + if (new String(exchangeTempArray, StandardCharsets.UTF_8).toLowerCase().endsWith(".tik")) + this.ticketID = i; + this.ncaFiles[i].setNcaFileName(Arrays.copyOf(exchangeTempArray, exchangeTempArray.length)); + } + randAccessFile.close(); + logPrinter.print("PFS Finished NSP file analyze for ["+nspFileName+"]", EMsgType.PASS); + } + /** + * Return file name as byte array + * */ + public byte[] getBytesNspFileName(){ + return nspFileName.getBytes(StandardCharsets.UTF_8); + } + /** + * Return file name length as byte array + * */ + public byte[] getBytesNspFileNameLength(){ + return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(getBytesNspFileName().length).array(); + } + /** + * Return NCA count inside of file as byte array + * */ + public byte[] getBytesCountOfNca(){ + return ByteBuffer.allocate(4).order(ByteOrder.LITTLE_ENDIAN).putInt(ncaFiles.length).array(); + } + /** + * Return NCA count inside of file as int + * */ + public int getIntCountOfNca(){ + return ncaFiles.length; + } + /** + * Return requested-by-number NCA file inside of file + * */ + public NCAFile getNca(int ncaNumber){ + return ncaFiles[ncaNumber]; + } + /** + * Return bodySize + * */ + public long getBodySize(){ + return bodySize; + } + /** + * Return special NCA file: ticket + * (sugar) + * */ + public int getNcaTicketID(){ + return ticketID; + } +} diff --git a/src/main/java/nsusbloader/USB/UsbCommunications.java b/src/main/java/nsusbloader/USB/UsbCommunications.java index 97df6a6..4206d13 100644 --- a/src/main/java/nsusbloader/USB/UsbCommunications.java +++ b/src/main/java/nsusbloader/USB/UsbCommunications.java @@ -54,8 +54,10 @@ public class UsbCommunications extends Task { if (protocol.equals("TinFoil")) module = new TinFoil(handler, nspMap, this, logPrinter); - else + else if (protocol.equals("GoldLeaf")) module = new GoldLeaf(handler, nspMap, this, logPrinter, nspFilterForGl); + else + module = new GoldLeaf_05(handler, nspMap, this, logPrinter); usbConnect.close(); diff --git a/src/main/resources/SettingsTab.fxml b/src/main/resources/SettingsTab.fxml index 7e3532f..4664798 100644 --- a/src/main/resources/SettingsTab.fxml +++ b/src/main/resources/SettingsTab.fxml @@ -94,6 +94,12 @@