v0.8 GoldLeaf v0.5 support. Optional. Updated since v0.5.2.

This commit is contained in:
Dmitry Isaenko 2019-09-26 05:15:37 +03:00
parent 1d0a6086de
commit bdb91b0e0a
13 changed files with 614 additions and 9 deletions

View file

@ -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:

View file

@ -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);}
}

View file

@ -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()
);
}
}

View file

@ -61,8 +61,16 @@ public class SettingsController implements Initializable {
@FXML
private ChoiceBox<String> langCB;
@FXML
private CheckBox glOldVerCheck;
@FXML
private ChoiceBox<String> 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 "";
}
}

View file

@ -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);

View file

@ -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<Void> task;
private LogPrinter logPrinter;
private EFileStatus status = EFileStatus.FAILED;
private RandomAccessFile raf; // NSP File
GoldLeaf_05(DeviceHandle handler, LinkedHashMap<String, File> nspMap, Task<Void> 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;
}
}

View file

@ -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; }
}

View file

@ -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<Integer, Long> ncaNameOffsets = new LinkedHashMap<>();
int offset;
long nca_offset;
long nca_size;
long nca_name_offset;
for (int i=0; i<filesCount; i++){
if (randAccessFile.read(ncaInfoArr) == 24) {
logPrinter.print("PFS Read NCA inside NSP: " + i, EMsgType.PASS);
}
else {
logPrinter.print("PFS Read NCA inside NSP: "+i, EMsgType.FAIL);
randAccessFile.close();
throw new Exception("Unable to read NCA inside NSP");
}
offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 0, 4)).order(ByteOrder.LITTLE_ENDIAN).getInt();
nca_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 4, 12)).order(ByteOrder.LITTLE_ENDIAN).getLong();
nca_size = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 12, 20)).order(ByteOrder.LITTLE_ENDIAN).getLong();
nca_name_offset = ByteBuffer.wrap(Arrays.copyOfRange(ncaInfoArr, 20, 24)).order(ByteOrder.LITTLE_ENDIAN).getInt(); // yes, cast from int to long.
logPrinter.print(" Padding check", offset == 0?EMsgType.PASS:EMsgType.WARNING);
logPrinter.print(" NCA offset check: "+nca_offset, nca_offset >= 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<Byte> ncaFN; // Temporary
byte[] b = new byte[1]; // Temporary
for (int i=0; i<filesCount; i++){
ncaFN = new ArrayList<>();
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;
}
}

View file

@ -54,8 +54,10 @@ public class UsbCommunications extends Task<Void> {
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();

View file

@ -94,6 +94,12 @@
</HBox>
<CheckBox fx:id="tfXciSpprtCb" mnemonicParsing="false" text="%tab2_Cb_AllowXci" />
<Label disable="true" text="%tab2_Lbl_AllowXciDesc" wrapText="true" />
<HBox alignment="CENTER_LEFT" spacing="5.0">
<children>
<CheckBox fx:id="glOldVerCheck" mnemonicParsing="false" text="%tab2_Cb_UseOldGlVersion" />
<ChoiceBox fx:id="glOldVerChoice" prefWidth="75.0" />
</children>
</HBox>
</children>
<padding>
<Insets bottom="5.0" left="5.0" right="5.0" top="5.0" />

View file

@ -43,3 +43,4 @@ tab2_Lbl_AllowXciDesc=Used by some third-party applications that support XCI and
tab2_Lbl_Language=Language
windowBodyRestartToApplyLang=Please restart application to apply changes.
tab2_Cb_GLshowNspOnly=Show only *.nsp in GoldLeaf.
tab2_Cb_UseOldGlVersion=Use old GoldLeaf version

View file

@ -43,4 +43,5 @@ tab2_Lbl_AllowXciDesc=\u0418\u0441\u043F\u043E\u043B\u044C\u0437\u0443\u0435\u04
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.
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

View file

@ -43,3 +43,4 @@ tab2_Lbl_AllowXciDesc=\u0412\u0438\u043A\u043E\u0440\u0438\u0441\u0442\u043E\u04
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.
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