1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

Add NSP transfer mode to USB code + implement SendNspHeader USB command.

Completely untested.
This commit is contained in:
Pablo Curiel 2020-10-26 02:39:33 -04:00
parent da5290189b
commit 5c5f388feb
9 changed files with 1416 additions and 21 deletions

File diff suppressed because it is too large Load diff

View file

@ -359,7 +359,7 @@ static bool sendFileData(const char *path, void *data, size_t data_size)
return false;
}
if (!usbSendFileProperties(data_size, path))
if (!usbSendFilePropertiesCommon(data_size, path))
{
consolePrint("failed to send file properties for \"%s\"!\n", path);
return false;
@ -515,7 +515,7 @@ static bool sendGameCardImageViaUsb(void)
}
snprintf(path, MAX_ELEMENTS(path), "%s (%s) (%s) (%s).xci", filename, g_appendKeyArea ? "keyarea" : "keyarealess", g_keepCertificate ? "cert" : "certless", g_trimDump ? "trimmed" : "untrimmed");
if (!usbSendFileProperties(gc_size, path))
if (!usbSendFilePropertiesCommon(gc_size, path))
{
consolePrint("failed to send file properties for \"%s\"!\n", path);
goto end;

View file

@ -105,7 +105,7 @@ static void read_thread_func(void *arg)
if (shared_data->write_error) break;
/* Send current file properties */
shared_data->read_error = !usbSendFileProperties(file_entry->size, path);
shared_data->read_error = !usbSendFilePropertiesCommon(file_entry->size, path);
if (shared_data->read_error)
{
condvarWakeAll(&g_writeCondvar);

View file

@ -1,8 +1,42 @@
diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
index dd2a1bc..4a3d7fd 100644
index dd2a1bc..58add89 100644
--- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
+++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java
@@ -111,6 +111,9 @@ class NxdtUsbAbi1 {
@@ -42,7 +42,7 @@ class NxdtUsbAbi1 {
private final boolean isWindows;
private boolean isWindows10;
- private static final int NXDT_MAX_DIRECTIVE_SIZE = 0x1000;
+ private static final int NXDT_MAX_DIRECTIVE_SIZE = 0x800000;
private static final int NXDT_FILE_CHUNK_SIZE = 0x800000;
private static final int NXDT_FILE_PROPERTIES_MAX_NAME_LENGTH = 0x300;
@@ -51,6 +51,7 @@ class NxdtUsbAbi1 {
private static final int CMD_HANDSHAKE = 0;
private static final int CMD_SEND_FILE_PROPERTIES = 1;
+ private static final int CMD_SEND_NSP_HEADER = 2;
private static final int CMD_ENDSESSION = 3;
// Standard set of possible replies
@@ -79,9 +80,15 @@ class NxdtUsbAbi1 {
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00 };
- private short endpointMaxPacketSize;
+ private short endpointMaxPacketSize = 0;
private static final int NXDT_USB_TIMEOUT = 5000;
+
+ private boolean nspTransferMode = false;
+ private long nspSize = 0;
+ private int nspHeaderSize = 0;
+ private long nspRemainingSize = 0;
+ private File nspFile = null;
public NxdtUsbAbi1(DeviceHandle handler,
ILogPrinter logPrinter,
@@ -111,6 +118,9 @@ class NxdtUsbAbi1 {
DeviceInformation deviceInformation = DeviceInformation.build(handlerNS);
NsUsbEndpointDescriptor endpointInDescriptor = deviceInformation.getSimplifiedDefaultEndpointDescriptorIn();
this.endpointMaxPacketSize = endpointInDescriptor.getwMaxPacketSize();
@ -12,7 +46,17 @@ index dd2a1bc..4a3d7fd 100644
}
private void readLoop(){
@@ -187,16 +190,8 @@ class NxdtUsbAbi1 {
@@ -134,6 +144,9 @@ class NxdtUsbAbi1 {
case CMD_SEND_FILE_PROPERTIES:
handleSendFileProperties(directive);
break;
+ case CMD_SEND_NSP_HEADER:
+ handleSendNspHeader(directive);
+ break;
case CMD_ENDSESSION:
logPrinter.print("Session successfully ended.", EMsgType.PASS);
return;
@@ -187,30 +200,52 @@ class NxdtUsbAbi1 {
writeUsb(USBSTATUS_UNSUPPORTED_ABI);
throw new Exception("ABI v"+versionABI+" is not supported in current version.");
}
@ -31,6 +75,197 @@ index dd2a1bc..4a3d7fd 100644
}
private void handleSendFileProperties(byte[] message) throws Exception{
final long fileSize = getLElong(message, 0x10);
final int fileNameLen = getLEint(message, 0x18);
+ final int headerSize = getLEint(message, 0x1C);
String filename = new String(message, 0x20, fileNameLen, StandardCharsets.UTF_8);
+ if (!this.nspTransferMode && fileSize > 0 && headerSize >= fileSize) {
+ writeUsb(USBSTATUS_MALFORMED_REQUEST);
+ logPrinter.print("NSP header size non-zero in NSP transfer mode!", EMsgType.FAIL);
+ return;
+ }
+
+ if (this.nspTransferMode && headerSize > 0) {
+ writeUsb(USBSTATUS_MALFORMED_REQUEST);
+ logPrinter.print("NSP header size non-zero in NSP transfer mode!", EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
if (fileNameLen <= 0 || fileNameLen > NXDT_FILE_PROPERTIES_MAX_NAME_LENGTH){
writeUsb(USBSTATUS_MALFORMED_REQUEST);
logPrinter.print("Invalid filename length!", EMsgType.FAIL);
+ resetNspInfo();
return;
}
+
// TODO: Note, in case of a big amount of small files performace decreses dramatically. It's better to handle this only in case of 1-big-file-transfer
- logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO);
+ if (!this.nspTransferMode) {
+ logPrinter.print("Receiving: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO);
+ } else {
+ logPrinter.print("Receiving NSP file entry: '"+filename+"' ("+fileSize+" b)", EMsgType.INFO);
+ }
+
+ if (!this.nspTransferMode && fileSize > 0 && headerSize > 0) {
+ // Enable NSP transfer mode
+ this.nspTransferMode = true;
+ this.nspSize = fileSize;
+ this.nspRemainingSize = (fileSize - headerSize);
+ this.nspHeaderSize = headerSize;
+ this.nspFile = null;
+ }
+
// If RomFs related
if (isRomFs(filename)) {
if (isWindows)
@@ -225,24 +260,48 @@ class NxdtUsbAbi1 {
filename = saveToPath + filename;
}
- File fileToDump = new File(filename);
- // Check if enough space
- if (fileToDump.getParentFile().getFreeSpace() <= fileSize){
- writeUsb(USBSTATUS_HOSTIOERROR);
- logPrinter.print("Not enough space on selected volume. Need: "+fileSize+
- " while available: "+fileToDump.getParentFile().getFreeSpace(), EMsgType.FAIL);
- return;
- }
- // Check if FS is NOT read-only
- if (! (fileToDump.canWrite() || fileToDump.createNewFile()) ){
- writeUsb(USBSTATUS_HOSTIOERROR);
- logPrinter.print("Unable to write into selected volume: "+fileToDump.getAbsolutePath(), EMsgType.FAIL);
- return;
+ File fileToDump;
+
+ if (!this.nspTransferMode || (this.nspTransferMode && this.nspFile == null)) {
+ fileToDump = new File(filename);
+
+ // Check if enough space
+ if (fileToDump.getParentFile().getFreeSpace() <= fileSize){
+ writeUsb(USBSTATUS_HOSTIOERROR);
+ logPrinter.print("Not enough space on selected volume. Need: "+fileSize+
+ " while available: "+fileToDump.getParentFile().getFreeSpace(), EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
+ // Check if FS is NOT read-only
+ if (! (fileToDump.canWrite() || fileToDump.createNewFile()) ){
+ writeUsb(USBSTATUS_HOSTIOERROR);
+ logPrinter.print("Unable to write into selected volume: "+fileToDump.getAbsolutePath(), EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
+ // Delete file if it exists
+ if (fileToDump.exists()) fileToDump.delete();
+
+ if (this.nspTransferMode) {
+ // Update NSP file object
+ this.nspFile = fileToDump;
+
+ // Write padding
+ try (FileOutputStream fos = new FileOutputStream(this.nspFile, false)) {
+ byte[] reserved = new byte[this.nspHeaderSize];
+ fos.write(reserved);
+ }
+ }
+ } else {
+ fileToDump = this.nspFile;
}
writeUsb(USBSTATUS_SUCCESS);
- if (fileSize == 0)
+ if (fileSize == 0 || (this.nspTransferMode && fileSize == this.nspSize))
return;
dumpFile(fileToDump, fileSize);
@@ -251,6 +310,49 @@ class NxdtUsbAbi1 {
}
+ private void handleSendNspHeader(byte[] message) throws Exception{
+ final int headerSize = getLEint(message, 0x8);
+
+ if (!this.nspTransferMode) {
+ writeUsb(USBSTATUS_MALFORMED_REQUEST);
+ logPrinter.print("Received NSP send header request outside of NSP transfer mode!", EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
+ if (this.nspRemainingSize > 0) {
+ writeUsb(USBSTATUS_MALFORMED_REQUEST);
+ logPrinter.print("Received NSP send header request without receiving all NSP file entry data!", EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
+ if (headerSize != this.nspHeaderSize) {
+ writeUsb(USBSTATUS_MALFORMED_REQUEST);
+ logPrinter.print("Received NSP header size mismatch! "+headerSize+" != "+this.nspHeaderSize, EMsgType.FAIL);
+ resetNspInfo();
+ return;
+ }
+
+ try (RandomAccessFile raf = new RandomAccessFile(this.nspFile, "rw")) {
+ byte[] headerData = Arrays.copyOfRange(message, 0x10, headerSize + 0x10);
+ raf.seek(0);
+ raf.write(headerData);
+ }
+
+ resetNspInfo();
+
+ writeUsb(USBSTATUS_SUCCESS);
+ }
+
+ private void resetNspInfo(){
+ this.nspTransferMode = false;
+ this.nspSize = 0;
+ this.nspHeaderSize = 0;
+ this.nspRemainingSize = 0;
+ this.nspFile = null;
+ }
+
private int getLEint(byte[] bytes, int fromOffset){
return ByteBuffer.wrap(bytes, fromOffset, 0x4).order(ByteOrder.LITTLE_ENDIAN).getInt();
}
@@ -279,7 +381,7 @@ class NxdtUsbAbi1 {
// @see https://bugs.openjdk.java.net/browse/JDK-8146538
private void dumpFile(File file, long size) throws Exception{
- FileOutputStream fos = new FileOutputStream(file, true);
+ FileOutputStream fos = new FileOutputStream(file, this.nspTransferMode);
try (BufferedOutputStream bos = new BufferedOutputStream(fos)) {
FileDescriptor fd = fos.getFD();
@@ -296,15 +398,21 @@ class NxdtUsbAbi1 {
bufferSize = readBuffer.length;
received += bufferSize;
- logPrinter.updateProgress((double)received / (double)size);
+ if (!this.nspTransferMode) {
+ logPrinter.updateProgress((double)received / (double)size);
+ } else {
+ this.nspRemainingSize -= bufferSize;
+ logPrinter.updateProgress((double)(this.nspSize - this.nspRemainingSize) / (double)this.nspSize);
+ }
}
int lastChunkSize = (int)(size - received) + 1;
readBuffer = readUsbFileDebug(lastChunkSize);
bos.write(readBuffer);
if (isWindows10)
fd.sync();
+ if (this.nspTransferMode) this.nspRemainingSize -= (lastChunkSize - 1);
} finally {
- logPrinter.updateProgress(1.0);
+ if (!this.nspTransferMode || (this.nspTransferMode && this.nspRemainingSize == 0)) logPrinter.updateProgress(1.0);
}
}
/* Handle Zero-length terminator
diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml
index a2d42d6..9114c3d 100644
--- a/src/main/resources/NSLMain.fxml

View file

@ -168,4 +168,22 @@ NX_INLINE void pfsInitializeFileContext(PartitionFileSystemFileContext *ctx)
ctx->header.magic = __builtin_bswap32(PFS0_MAGIC);
}
NX_INLINE u32 pfsGetEntryCountFromFileContext(PartitionFileSystemFileContext *ctx)
{
return (ctx ? ctx->header.entry_count : 0);
}
NX_INLINE PartitionFileSystemEntry *pfsGetEntryByIndexFromFileContext(PartitionFileSystemFileContext *ctx, u32 idx)
{
if (idx >= pfsGetEntryCountFromFileContext(ctx)) return NULL;
return &(ctx->entries[idx]);
}
NX_INLINE char *pfsGetEntryNameByIndexFromFileContext(PartitionFileSystemFileContext *ctx, u32 idx)
{
PartitionFileSystemEntry *fs_entry = pfsGetEntryByIndexFromFileContext(ctx, idx);
if (!fs_entry || !ctx->name_table) return NULL;
return (ctx->name_table + fs_entry->name_offset);
}
#endif /* __PFS_H__ */

View file

@ -52,7 +52,7 @@ typedef struct {
typedef enum {
UsbCommandType_StartSession = 0,
UsbCommandType_SendFileProperties = 1,
UsbCommandType_SendNspHeader = 2, ///< Needs to be implemented.
UsbCommandType_SendNspHeader = 2,
UsbCommandType_EndSession = 3
} UsbCommandType;
@ -74,7 +74,7 @@ typedef struct {
typedef struct {
u64 file_size;
u32 filename_length;
u8 reserved_1[0x4];
u32 nsp_header_size;
char filename[FS_MAX_PATH];
u8 reserved_2[0xF];
} UsbCommandSendFileProperties;
@ -112,7 +112,7 @@ static bool g_usbDeviceInterfaceInitialized = false;
static Event *g_usbStateChangeEvent = NULL;
static Thread g_usbDetectionThread = {0};
static UEvent g_usbDetectionThreadExitEvent = {0}, g_usbTimeoutEvent = {0};
static bool g_usbHostAvailable = false, g_usbSessionStarted = false, g_usbDetectionThreadExitFlag = false;
static bool g_usbHostAvailable = false, g_usbSessionStarted = false, g_usbDetectionThreadExitFlag = false, g_nspTransferMode = false;
static atomic_bool g_usbDetectionThreadCreated = false;
static u8 *g_usbTransferBuffer = NULL;
@ -239,7 +239,7 @@ bool usbIsReady(void)
return ret;
}
bool usbSendFileProperties(u64 file_size, const char *filename)
bool usbSendFileProperties(u64 file_size, const char *filename, u32 nsp_header_size)
{
rwlockWriteLock(&g_usbDeviceLock);
rwlockWriteLock(&(g_usbDeviceInterface.lock));
@ -248,10 +248,11 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
UsbCommandSendFileProperties *cmd_block = NULL;
size_t cmd_size = 0;
u32 status = UsbStatusType_Success;
u32 filename_length = 0;
size_t filename_length = 0;
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || g_usbTransferRemainingSize > 0 || !filename || \
!(filename_length = (u32)strlen(filename)) || filename_length >= FS_MAX_PATH)
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || !filename || \
!(filename_length = strlen(filename)) || filename_length >= FS_MAX_PATH || (!g_nspTransferMode && ((file_size && nsp_header_size >= file_size) || g_usbTransferRemainingSize)) || \
(g_nspTransferMode && nsp_header_size))
{
LOGFILE("Invalid parameters!");
goto end;
@ -263,7 +264,8 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
memset(cmd_block, 0, sizeof(UsbCommandSendFileProperties));
cmd_block->file_size = file_size;
cmd_block->filename_length = filename_length;
cmd_block->filename_length = (u32)filename_length;
cmd_block->nsp_header_size = nsp_header_size;
sprintf(cmd_block->filename, "%s", filename);
cmd_size = (sizeof(UsbCommandHeader) + sizeof(UsbCommandSendFileProperties));
@ -274,11 +276,14 @@ bool usbSendFileProperties(u64 file_size, const char *filename)
ret = true;
g_usbTransferRemainingSize = file_size;
g_usbTransferWrittenSize = 0;
if (!g_nspTransferMode) g_nspTransferMode = (file_size && nsp_header_size);
} else {
usbLogStatusDetail(status);
}
end:
if (!ret && g_nspTransferMode) g_nspTransferMode = false;
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
@ -371,8 +376,12 @@ end:
/* Disable ZLT if it was previously enabled. */
if (zlt_required) usbSetZltPacket(false);
/* Reset remaining and written sizes in case of errors. */
if (!ret) g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
/* Reset variables in case of errors. */
if (!ret)
{
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
g_nspTransferMode = false;
}
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
@ -380,13 +389,64 @@ end:
return ret;
}
bool usbSendNspHeader(void *nsp_header, u32 nsp_header_size)
{
rwlockWriteLock(&g_usbDeviceLock);
rwlockWriteLock(&(g_usbDeviceInterface.lock));
size_t cmd_size = 0;
u32 status = UsbStatusType_Success;
bool ret = false, zlt_required = false;
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || g_usbTransferRemainingSize || \
!g_nspTransferMode || !nsp_header || !nsp_header_size || nsp_header_size > (USB_TRANSFER_BUFFER_SIZE - sizeof(UsbCommandHeader)))
{
LOGFILE("Invalid parameters!");
goto end;
}
/* Disable NSP transfer mode right away. */
g_nspTransferMode = false;
/* Prepare command data. */
usbPrepareCommandHeader(UsbCommandType_SendNspHeader, nsp_header_size);
memcpy(g_usbTransferBuffer + sizeof(UsbCommandHeader), nsp_header, nsp_header_size);
cmd_size = (sizeof(UsbCommandHeader) + nsp_header_size);
/* Determine if we'll need to set a Zero Length Termination (ZLT) packet. */
zlt_required = IS_ALIGNED(cmd_size, g_usbEndpointMaxPacketSize);
if (zlt_required)
{
usbSetZltPacket(true);
//LOGFILE("ZLT enabled. SendNspHeader command size: 0x%lX.", cmd_size);
}
/* Send command. */
ret = ((status = usbSendCommand(cmd_size)) == UsbStatusType_Success);
if (!ret) usbLogStatusDetail(status);
/* Disable ZLT if it was previously enabled. */
if (zlt_required) usbSetZltPacket(false);
end:
rwlockWriteUnlock(&(g_usbDeviceInterface.lock));
rwlockWriteUnlock(&g_usbDeviceLock);
return ret;
}
void usbCancelFileTransfer(void)
{
rwlockWriteLock(&g_usbDeviceLock);
rwlockWriteLock(&(g_usbDeviceInterface.lock));
rwlockWriteLock(&(g_usbDeviceInterface.lock_in));
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || !g_usbTransferRemainingSize) goto end;
if (!g_usbTransferBuffer || !g_usbDeviceInterfaceInitialized || !g_usbDeviceInterface.initialized || !g_usbHostAvailable || !g_usbSessionStarted || (!g_usbTransferRemainingSize && \
!g_nspTransferMode)) goto end;
/* Reset variables. */
g_usbTransferRemainingSize = g_usbTransferWrittenSize = 0;
g_nspTransferMode = false;
/* Disable ZLT, just in case it was previously enabled. */
usbDsEndpoint_SetZlt(g_usbDeviceInterface.endpoint_in, false);

View file

@ -44,14 +44,30 @@ void *usbAllocatePageAlignedBuffer(size_t size);
bool usbIsReady(void);
/// Sends file properties to the host device before starting a file data transfer. Must be called before usbSendFileData().
bool usbSendFileProperties(u64 file_size, const char *filename);
/// If 'nsp_header_size' is greater than zero, NSP transfer mode will be enabled. The file will be treated as a NSP and this value will be taken as its full Partition FS header size.
/// Under NSP transfer mode, this function must be called right before transferring data from each NSP file entry to the host device, which should in turn write it all to the same output file.
/// Calling this function after NSP transfer mode has been enabled with a 'nsp_header_size' value greater than zero will result in an error.
/// The host device should immediately write 'nsp_header_size' padding at the start of the output file and start listening for further usbSendFileProperties() calls, or a usbSendNspHeader() call.
bool usbSendFileProperties(u64 file_size, const char *filename, u32 nsp_header_size);
/// Performs a file data transfer. Must be continuously called after usbSendFileProperties() until all file data has been transferred.
/// Data chunk size must not exceed USB_TRANSFER_BUFFER_SIZE.
/// If the last file data chunk is aligned to the endpoint max packet size, the host device should expect a Zero Length Termination (ZLT) packet.
bool usbSendFileData(void *data, u64 data_size);
/// Sends NSP header data to the host device, making it rewind the NSP file pointer to write this data, essentially finishing the NSP transfer process.
/// Must be called after the data from all NSP file entries has been transferred using both usbSendFileProperties() and usbSendFileData() calls.
/// If the sum of the USB command header and NSP header sizes is aligned to the endpoint max packet size, the host device should expect a Zero Length Termination (ZLT) packet.
bool usbSendNspHeader(void *nsp_header, u32 nsp_header_size);
/// Used to cancel an ongoing file transfer by stalling the input (write) USB endpoint.
/// A new USB session must be established afterwards if USB communication with a host device is required.
void usbCancelFileTransfer(void);
/// Nice and small wrapper for non-NSP files.
NX_INLINE bool usbSendFilePropertiesCommon(u64 file_size, const char *filename)
{
return usbSendFileProperties(file_size, filename, 0);
}
#endif /* __USB_H__ */

View file

@ -688,14 +688,15 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element)
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension)
{
if (!prefix || !*prefix || !filename || !*filename || !extension || !*extension)
if (!filename || !*filename || !extension || !*extension)
{
LOGFILE("Invalid parameters!");
return NULL;
}
char *path = NULL;
size_t path_len = (strlen(prefix) + strlen(filename) + strlen(extension) + 1);
size_t path_len = (strlen(filename) + strlen(extension) + 1);
if (prefix && *prefix) path_len += strlen(prefix);
if (!(path = calloc(path_len, sizeof(char))))
{
@ -703,7 +704,9 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex
return NULL;
}
sprintf(path, "%s%s%s", prefix, filename, extension);
if (prefix && *prefix) strcat(path, prefix);
strcat(path, filename);
strcat(path, extension);
return path;
}