From de076f49086a598759e08f46f67874cf31af6384 Mon Sep 17 00:00:00 2001 From: Pablo Curiel Date: Tue, 16 Feb 2021 08:22:14 -0400 Subject: [PATCH] More USB changes. * usb: copy command ID and block size before moving command data within the USB transfer buffer. * nsp_dumper_usb: now spans a background thread for the dump process, progress is now displayed, process can now be cancelled by holding B, updated to properly make use of the new usbCancelFileTransfer() behavior. * usb_gc_dumper: updated to properly make use of the new usbCancelFileTransfer() behavior. * usb_romfs_dumper: updated to properly make use of the new usbCancelFileTransfer() behavior. * Updated ns-usbloader patch. Must be used on commit `8771d551a4e6fa2d645e519d504a377e34cbd730`. --- code_templates/nsp_dumper_usb.c | 310 ++++++++++++++++++++-------- code_templates/usb_gc_dumper.c | 2 +- code_templates/usb_romfs_dumper.c | 2 +- nsul_nxdt_patch.diff | 330 +++++++++++++++++++++++++----- source/usb.c | 22 +- 5 files changed, 515 insertions(+), 151 deletions(-) diff --git a/code_templates/nsp_dumper_usb.c b/code_templates/nsp_dumper_usb.c index ebacd59..037996c 100644 --- a/code_templates/nsp_dumper_usb.c +++ b/code_templates/nsp_dumper_usb.c @@ -30,6 +30,15 @@ #define BLOCK_SIZE 0x800000 +typedef struct +{ + void *data; + size_t data_written; + size_t total_size; + bool error; + bool transfer_cancelled; +} ThreadSharedData; + static const char *dump_type_strings[] = { "dump base application", "dump update", @@ -55,42 +64,30 @@ static options_t options[] = { static const u32 options_count = MAX_ELEMENTS(options); +static Mutex g_conMutex = 0; + static void consolePrint(const char *text, ...) { + mutexLock(&g_conMutex); va_list v; va_start(v, text); vfprintf(stdout, text, v); va_end(v); - consoleUpdate(NULL); + mutexUnlock(&g_conMutex); } -static void nspDump(TitleInfo *title_info) +static void consoleRefresh(void) { - if (!title_info || !title_info->content_count || !title_info->content_infos) return; + mutexLock(&g_conMutex); + consoleUpdate(NULL); + mutexUnlock(&g_conMutex); +} + +static void dump_thread_func(void *arg) +{ + ThreadSharedData *shared_data = (ThreadSharedData*)arg; - consoleClear(); - - TitleApplicationMetadata *app_metadata = title_info->app_metadata; - - printf("%s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ - (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); - - if (app_metadata) - { - printf("name: %s\n", app_metadata->lang_entry.name); - printf("publisher: %s\n", app_metadata->lang_entry.author); - } - - printf("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); - printf("title id: %016lX\n", title_info->meta_key.id); - printf("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.major, title_info->version.minor, title_info->version.micro, title_info->version.major_relstep, \ - title_info->version.minor_relstep); - printf("content count: %u\n", title_info->content_count); - printf("size: %s\n", title_info->size_str); - printf("______________________________\n\n"); - printf("dump options:\n\n"); - for(u32 i = 0; i < options_count; i++) printf("%s: %s\n", options[i].str, options[i].val ? "yes" : "no"); - printf("______________________________\n\n"); + TitleInfo *title_info = NULL; bool set_download_type = options[0].val; bool remove_console_data = options[1].val; @@ -99,7 +96,7 @@ static void nspDump(TitleInfo *title_info) bool patch_sua = options[4].val; bool patch_screenshot = options[5].val; bool patch_video_capture = options[6].val; - bool success = false, usb_conn = false; + bool success = false; u8 *buf = NULL; char *dump_name = NULL, *path = NULL; @@ -110,13 +107,13 @@ static void nspDump(TitleInfo *title_info) ContentMetaContext cnmt_ctx = {0}; ProgramInfoContext *program_info_ctx = NULL; - u32 program_idx = 0, program_count = titleGetContentCountByType(title_info, NcmContentType_Program); + u32 program_idx = 0, program_count = 0; NacpContext *nacp_ctx = NULL; - u32 control_idx = 0, control_count = titleGetContentCountByType(title_info, NcmContentType_Control); + u32 control_idx = 0, control_count = 0; LegalInfoContext *legal_info_ctx = NULL; - u32 legal_info_idx = 0, legal_info_count = titleGetContentCountByType(title_info, NcmContentType_LegalInformation); + u32 legal_info_idx = 0, legal_info_count = 0; Ticket tik = {0}; TikCommonBlock *tik_common_block = NULL; @@ -134,6 +131,12 @@ static void nspDump(TitleInfo *title_info) Sha256Context sha256_ctx = {0}; u8 sha256_hash[SHA256_HASH_SIZE] = {0}; + if (!shared_data || !(title_info = (TitleInfo*)shared_data->data) || !title_info->content_count || !title_info->content_infos) goto end; + + program_count = titleGetContentCountByType(title_info, NcmContentType_Program); + control_count = titleGetContentCountByType(title_info, NcmContentType_Control); + legal_info_count = titleGetContentCountByType(title_info, NcmContentType_LegalInformation); + /* Allocate memory for the dump process. */ if (!(buf = usbAllocatePageAlignedBuffer(BLOCK_SIZE))) { @@ -461,31 +464,7 @@ static void nspDump(TitleInfo *title_info) nsp_size = (nsp_header_size + pfs_file_ctx.fs_size); consolePrint("nsp header size: 0x%lX | nsp size: 0x%lX\n", nsp_header_size, nsp_size); - consolePrint("waiting for usb connection... "); - - time_t start = time(NULL); - - while(true) - { - time_t now = time(NULL); - if ((now - start) >= 10) break; - consolePrint("%lu ", now - start); - - if ((usb_conn = usbIsReady())) break; - utilsSleep(1); - } - - consolePrint("\n"); - - if (!usb_conn) - { - consolePrint("usb connection failed\n"); - goto end; - } - - consolePrint("dump process started. please wait...\n"); - - start = time(NULL); + consolePrint("dump process started, please wait. hold b to cancel.\n"); if (!usbSendFileProperties(nsp_size, path, (u32)nsp_header_size)) { @@ -495,6 +474,9 @@ static void nspDump(TitleInfo *title_info) nsp_offset += nsp_header_size; + // set nsp size + shared_data->total_size = nsp_size; + // write ncas for(u32 i = 0; i < title_info->content_count; i++) { @@ -519,8 +501,14 @@ static void nspDump(TitleInfo *title_info) goto end; } - for(u64 offset = 0; offset < cur_nca_ctx->content_size; offset += blksize, nsp_offset += blksize) + for(u64 offset = 0; offset < cur_nca_ctx->content_size; offset += blksize, nsp_offset += blksize, shared_data->data_written += blksize) { + if (shared_data->transfer_cancelled) + { + usbCancelFileTransfer(); + goto end; + } + if ((cur_nca_ctx->content_size - offset) < blksize) blksize = (cur_nca_ctx->content_size - offset); // read nca chunk @@ -606,6 +594,7 @@ static void nspDump(TitleInfo *title_info) } nsp_offset += cnmt_ctx.authoring_tool_xml_size; + shared_data->data_written += cnmt_ctx.authoring_tool_xml_size; // update cnmt xml pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, meta_nca_ctx->content_type_ctx_data_idx, meta_nca_ctx->content_id_str)) @@ -653,6 +642,7 @@ static void nspDump(TitleInfo *title_info) } nsp_offset += icon_ctx->icon_size; + shared_data->data_written += icon_ctx->icon_size; // update pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx++, cur_nca_ctx->content_id_str)) @@ -684,6 +674,7 @@ static void nspDump(TitleInfo *title_info) } nsp_offset += authoring_tool_xml_size; + shared_data->data_written += authoring_tool_xml_size; // update pfs entry name if (!pfsUpdateEntryNameFromFileContext(&pfs_file_ctx, data_idx, cur_nca_ctx->content_id_str)) @@ -704,6 +695,7 @@ static void nspDump(TitleInfo *title_info) } nsp_offset += tik.size; + shared_data->data_written += tik.size; // write cert tmp_name = pfsGetEntryNameByIndexFromFileContext(&pfs_file_ctx, pfs_file_ctx.header.entry_count - 1); @@ -714,6 +706,7 @@ static void nspDump(TitleInfo *title_info) } nsp_offset += raw_cert_chain_size; + shared_data->data_written += raw_cert_chain_size; } // write new pfs0 header @@ -729,13 +722,14 @@ static void nspDump(TitleInfo *title_info) goto end; } - start = (time(NULL) - start); - consolePrint("process successfully completed in %lu seconds!\n", start); + shared_data->data_written += nsp_header_size; success = true; end: - if (usb_conn && !success) usbCancelFileTransfer(); + consoleRefresh(); + + if (!success && !shared_data->transfer_cancelled) shared_data->error = true; pfsFreeFileContext(&pfs_file_ctx); @@ -768,6 +762,146 @@ end: if (dump_name) free(dump_name); if (buf) free(buf); + + threadExit(); +} + +static void nspDump(TitleInfo *title_info) +{ + if (!title_info) return; + + TitleApplicationMetadata *app_metadata = title_info->app_metadata; + + ThreadSharedData shared_data = {0}; + Thread dump_thread = {0}; + + time_t start = 0, btn_cancel_start_tmr = 0, btn_cancel_end_tmr = 0; + bool usb_conn = false, btn_cancel_cur_state = false, btn_cancel_prev_state = false; + + u64 prev_size = 0; + u8 prev_time = 0, percent = 0; + + consoleClear(); + + consolePrint("%s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ + (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); + + if (app_metadata) + { + consolePrint("name: %s\n", app_metadata->lang_entry.name); + consolePrint("publisher: %s\n", app_metadata->lang_entry.author); + } + + consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); + consolePrint("title id: %016lX\n", title_info->meta_key.id); + consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.major, title_info->version.minor, title_info->version.micro, title_info->version.major_relstep, \ + title_info->version.minor_relstep); + consolePrint("content count: %u\n", title_info->content_count); + consolePrint("size: %s\n", title_info->size_str); + consolePrint("______________________________\n\n"); + consolePrint("dump options:\n\n"); + for(u32 i = 0; i < options_count; i++) consolePrint("%s: %s\n", options[i].str, options[i].val ? "yes" : "no"); + consolePrint("______________________________\n\n"); + + // make sure we have a valid usb session + consolePrint("waiting for usb connection... "); + + start = time(NULL); + + while(true) + { + time_t now = time(NULL); + if ((now - start) >= 10) break; + + consolePrint("%lu ", now - start); + consoleRefresh(); + + if ((usb_conn = usbIsReady())) break; + utilsSleep(1); + } + + consolePrint("\n"); + + if (!usb_conn) + { + consolePrint("usb connection failed\n"); + return; + } + + // create dump thread + shared_data.data = title_info; + utilsCreateThread(&dump_thread, dump_thread_func, &shared_data, 2); + + while(!shared_data.total_size && !shared_data.error) svcSleepThread(10000000); // 10 ms + + if (shared_data.error) + { + utilsJoinThread(&dump_thread); + return; + } + + // start dump + start = time(NULL); + + while(shared_data.data_written < shared_data.total_size) + { + if (shared_data.error) break; + + time_t now = time(NULL); + struct tm *ts = localtime(&now); + size_t size = shared_data.data_written; + + utilsScanPads(); + btn_cancel_cur_state = (utilsGetButtonsHeld() & HidNpadButton_B); + + if (btn_cancel_cur_state && btn_cancel_cur_state != btn_cancel_prev_state) + { + btn_cancel_start_tmr = now; + } else + if (btn_cancel_cur_state && btn_cancel_cur_state == btn_cancel_prev_state) + { + btn_cancel_end_tmr = now; + if ((btn_cancel_end_tmr - btn_cancel_start_tmr) >= 3) + { + shared_data.transfer_cancelled = true; + break; + } + } else { + btn_cancel_start_tmr = btn_cancel_end_tmr = 0; + } + + btn_cancel_prev_state = btn_cancel_cur_state; + + if (prev_time == ts->tm_sec || prev_size == size) continue; + + percent = (u8)((size * 100) / shared_data.total_size); + + prev_time = ts->tm_sec; + prev_size = size; + + consolePrint("%lu / %lu (%u%%) | Time elapsed: %lu\n", size, shared_data.total_size, percent, (now - start)); + consoleRefresh(); + } + + start = (time(NULL) - start); + + consolePrint("\nwaiting for thread to join\n"); + utilsJoinThread(&dump_thread); + consolePrint("dump_thread done: %lu\n", time(NULL)); + + if (shared_data.error) + { + consolePrint("usb transfer error\n"); + return; + } + + if (shared_data.transfer_cancelled) + { + consolePrint("process cancelled\n"); + return; + } + + consolePrint("process completed in %lu seconds\n", start); } int main(int argc, char *argv[]) @@ -780,6 +914,7 @@ int main(int argc, char *argv[]) consoleInit(NULL); consolePrint("initializing...\n"); + consoleRefresh(); if (!utilsInitializeResources()) { @@ -806,6 +941,7 @@ int main(int argc, char *argv[]) } consolePrint("app metadata succeeded\n"); + consoleRefresh(); utilsSleep(1); @@ -813,72 +949,72 @@ int main(int argc, char *argv[]) { consoleClear(); - printf("press b to %s.\n", menu == 0 ? "exit" : "go back"); - printf("______________________________\n\n"); + consolePrint("press b to %s.\n", menu == 0 ? "exit" : "go back"); + consolePrint("______________________________\n\n"); if (menu == 0) { - printf("title: %u / %u\n", selected_idx + 1, app_count); - printf("selected title: %016lX - %s\n", app_metadata[selected_idx]->title_id, app_metadata[selected_idx]->lang_entry.name); + consolePrint("title: %u / %u\n", selected_idx + 1, app_count); + consolePrint("selected title: %016lX - %s\n", app_metadata[selected_idx]->title_id, app_metadata[selected_idx]->lang_entry.name); } else { - printf("title info:\n\n"); - printf("name: %s\n", app_metadata[title_idx]->lang_entry.name); - printf("publisher: %s\n", app_metadata[title_idx]->lang_entry.author); - printf("title id: %016lX\n", app_metadata[title_idx]->title_id); + consolePrint("title info:\n\n"); + consolePrint("name: %s\n", app_metadata[title_idx]->lang_entry.name); + consolePrint("publisher: %s\n", app_metadata[title_idx]->lang_entry.author); + consolePrint("title id: %016lX\n", app_metadata[title_idx]->title_id); if (menu == 2) { - printf("______________________________\n\n"); + consolePrint("______________________________\n\n"); if (title_info->previous || title_info->next) { - printf("press zl/l and/or zr/r to change the selected title\n"); - printf("title: %u / %u\n", list_idx, list_count); - printf("______________________________\n\n"); + consolePrint("press zl/l and/or zr/r to change the selected title\n"); + consolePrint("title: %u / %u\n", list_idx, list_count); + consolePrint("______________________________\n\n"); } - printf("selected %s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ + consolePrint("selected %s info:\n\n", title_info->meta_key.type == NcmContentMetaType_Application ? "base application" : \ (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); - printf("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); - if (title_info->meta_key.type != NcmContentMetaType_Application) printf("title id: %016lX\n", title_info->meta_key.id); - printf("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.major, title_info->version.minor, title_info->version.micro, title_info->version.major_relstep, \ + consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); + if (title_info->meta_key.type != NcmContentMetaType_Application) consolePrint("title id: %016lX\n", title_info->meta_key.id); + consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.major, title_info->version.minor, title_info->version.micro, title_info->version.major_relstep, \ title_info->version.minor_relstep); - printf("content count: %u\n", title_info->content_count); - printf("size: %s\n", title_info->size_str); + consolePrint("content count: %u\n", title_info->content_count); + consolePrint("size: %s\n", title_info->size_str); } } - printf("______________________________\n\n"); + consolePrint("______________________________\n\n"); u32 max_val = (menu == 0 ? app_count : (menu == 1 ? dump_type_strings_count : (1 + options_count))); for(u32 i = scroll; i < max_val; i++) { if (i >= (scroll + page_size)) break; - printf("%s", i == selected_idx ? " -> " : " "); + consolePrint("%s", i == selected_idx ? " -> " : " "); if (menu == 0) { - printf("%016lX - %s\n", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name); + consolePrint("%016lX - %s\n", app_metadata[i]->title_id, app_metadata[i]->lang_entry.name); } else if (menu == 1) { - printf("%s\n", dump_type_strings[i]); + consolePrint("%s\n", dump_type_strings[i]); } else if (menu == 2) { if (i == 0) { - printf("start nsp dump\n"); + consolePrint("start nsp dump\n"); } else { - printf("%s: < %s >\n", options[i - 1].str, options[i - 1].val ? "yes" : "no"); + consolePrint("%s: < %s >\n", options[i - 1].str, options[i - 1].val ? "yes" : "no"); } } } - printf("\n"); + consolePrint("\n"); - consoleUpdate(NULL); + consoleRefresh(); bool gc_update = false; u64 btn_down = 0, btn_held = 0; @@ -960,7 +1096,6 @@ int main(int argc, char *argv[]) } else if (menu == 3) { - consoleClear(); utilsChangeHomeButtonBlockStatus(true); nspDump(title_info); utilsChangeHomeButtonBlockStatus(false); @@ -969,6 +1104,7 @@ int main(int argc, char *argv[]) if (error || menu >= 3) { consolePrint("press any button to continue\n"); + consoleRefresh(); utilsWaitForButtonPress(0); menu--; } else { @@ -1043,6 +1179,8 @@ int main(int argc, char *argv[]) } out2: + consoleRefresh(); + if (menu != UINT32_MAX) { consolePrint("press any button to exit\n"); diff --git a/code_templates/usb_gc_dumper.c b/code_templates/usb_gc_dumper.c index 8187860..4ee4821 100644 --- a/code_templates/usb_gc_dumper.c +++ b/code_templates/usb_gc_dumper.c @@ -563,7 +563,6 @@ static bool sendGameCardImageViaUsb(void) if ((btn_cancel_end_tmr - btn_cancel_start_tmr) >= 3) { mutexLock(&g_fileMutex); - usbCancelFileTransfer(); shared_data.transfer_cancelled = true; mutexUnlock(&g_fileMutex); break; @@ -738,6 +737,7 @@ static void write_thread_func(void *arg) if (shared_data->read_error || shared_data->transfer_cancelled) { + if (shared_data->transfer_cancelled) usbCancelFileTransfer(); mutexUnlock(&g_fileMutex); break; } diff --git a/code_templates/usb_romfs_dumper.c b/code_templates/usb_romfs_dumper.c index 79a0272..4a0736c 100644 --- a/code_templates/usb_romfs_dumper.c +++ b/code_templates/usb_romfs_dumper.c @@ -181,6 +181,7 @@ static void write_thread_func(void *arg) if (shared_data->read_error || shared_data->transfer_cancelled) { + if (shared_data->transfer_cancelled) usbCancelFileTransfer(); mutexUnlock(&g_fileMutex); break; } @@ -585,7 +586,6 @@ int main(int argc, char *argv[]) if ((btn_cancel_end_tmr - btn_cancel_start_tmr) >= 3) { mutexLock(&g_fileMutex); - usbCancelFileTransfer(); shared_data.transfer_cancelled = true; mutexUnlock(&g_fileMutex); break; diff --git a/nsul_nxdt_patch.diff b/nsul_nxdt_patch.diff index 3c3fa22..970c89c 100644 --- a/nsul_nxdt_patch.diff +++ b/nsul_nxdt_patch.diff @@ -1,33 +1,38 @@ diff --git a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java -index dd2a1bc..58add89 100644 +index dd2a1bc..6c8f79e 100644 --- a/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java +++ b/src/main/java/nsusbloader/Utilities/nxdumptool/NxdtUsbAbi1.java -@@ -42,7 +42,7 @@ class NxdtUsbAbi1 { +@@ -42,7 +42,6 @@ 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 { +@@ -51,7 +50,9 @@ 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; +- private static final int CMD_ENDSESSION = 3; ++ private static final int CMD_CANCEL_FILE_TRANSFER = 2; ++ private static final int CMD_SEND_NSP_HEADER = 3; ++ private static final int CMD_ENDSESSION = 4; // Standard set of possible replies -@@ -79,9 +80,15 @@ class NxdtUsbAbi1 { + private static final byte[] USBSTATUS_SUCCESS = { 0x4e, 0x58, 0x44, 0x54, +@@ -79,9 +80,17 @@ 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 static final int NXDT_USB_TIMEOUT = 5000; ++ private static final int NXDT_USB_CMD_TIMEOUT = 1000; ++ private static final int NXDT_USB_DATA_TIMEOUT = 5000; ++ private static final int USB_BUF_ALIGNMENT = 0x1000; ++ + private boolean nspTransferMode = false; + private long nspSize = 0; + private int nspHeaderSize = 0; @@ -36,17 +41,28 @@ index dd2a1bc..58add89 100644 public NxdtUsbAbi1(DeviceHandle handler, ILogPrinter logPrinter, -@@ -111,6 +118,9 @@ class NxdtUsbAbi1 { +@@ -111,6 +120,9 @@ class NxdtUsbAbi1 { DeviceInformation deviceInformation = DeviceInformation.build(handlerNS); NsUsbEndpointDescriptor endpointInDescriptor = deviceInformation.getSimplifiedDefaultEndpointDescriptorIn(); this.endpointMaxPacketSize = endpointInDescriptor.getwMaxPacketSize(); + -+ USBSTATUS_SUCCESS[8] = (byte)(endpointMaxPacketSize & 0xFF); -+ USBSTATUS_SUCCESS[9] = (byte)((endpointMaxPacketSize >> 8) & 0xFF); ++ USBSTATUS_SUCCESS[8] = (byte)(this.endpointMaxPacketSize & 0xFF); ++ USBSTATUS_SUCCESS[9] = (byte)((this.endpointMaxPacketSize >> 8) & 0xFF); } private void readLoop(){ -@@ -134,6 +144,9 @@ class NxdtUsbAbi1 { +@@ -121,9 +133,7 @@ class NxdtUsbAbi1 { + + while (true){ + directive = readUsbDirective(); +- +- if (isInvalidDirective(directive)) +- continue; ++ if (directive == null || directive.length == 0) continue; + + command = getLEint(directive, 4); + +@@ -134,7 +144,11 @@ class NxdtUsbAbi1 { case CMD_SEND_FILE_PROPERTIES: handleSendFileProperties(directive); break; @@ -54,9 +70,40 @@ index dd2a1bc..58add89 100644 + handleSendNspHeader(directive); + break; case CMD_ENDSESSION: ++ writeUsb(USBSTATUS_SUCCESS); logPrinter.print("Session successfully ended.", EMsgType.PASS); return; -@@ -187,30 +200,52 @@ class NxdtUsbAbi1 { + default: +@@ -153,28 +167,6 @@ class NxdtUsbAbi1 { + } + } + +- private boolean isInvalidDirective(byte[] message) throws Exception{ +- if (message.length < 0x10){ +- writeUsb(USBSTATUS_MALFORMED_REQUEST); +- logPrinter.print("Directive is too small. Only "+message.length+" bytes received.", EMsgType.FAIL); +- return true; +- } +- +- if (! Arrays.equals(Arrays.copyOfRange(message, 0,4), MAGIC_NXDT)){ +- writeUsb(USBSTATUS_INVALID_MAGIC); +- logPrinter.print("Invalid 'MAGIC'", EMsgType.FAIL); +- return true; +- } +- +- int payloadSize = getLEint(message, 0x8); +- if (payloadSize + 0x10 != message.length){ +- writeUsb(USBSTATUS_MALFORMED_REQUEST); +- logPrinter.print("Invalid directive info block size. "+message.length+" bytes received while "+payloadSize+" expected.", EMsgType.FAIL); +- return true; +- } +- return false; +- } +- + private void performHandshake(byte[] message) throws Exception{ + final byte versionMajor = message[0x10]; + final byte versionMinor = message[0x11]; +@@ -187,30 +179,52 @@ class NxdtUsbAbi1 { writeUsb(USBSTATUS_UNSUPPORTED_ABI); throw new Exception("ABI v"+versionABI+" is not supported in current version."); } @@ -120,7 +167,7 @@ index dd2a1bc..58add89 100644 // If RomFs related if (isRomFs(filename)) { if (isWindows) -@@ -225,24 +260,48 @@ class NxdtUsbAbi1 { +@@ -225,30 +239,105 @@ class NxdtUsbAbi1 { filename = saveToPath + filename; } @@ -131,12 +178,6 @@ index dd2a1bc..58add89 100644 - 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)) { @@ -175,18 +216,31 @@ index dd2a1bc..58add89 100644 + } else { + fileToDump = this.nspFile; } - - writeUsb(USBSTATUS_SUCCESS); - -- if (fileSize == 0) +- // 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); ++ ++ writeUsb(USBSTATUS_SUCCESS); ++ + if (fileSize == 0 || (this.nspTransferMode && fileSize == this.nspSize)) return; ++ ++ if (dumpFile(fileToDump, fileSize)){ ++ writeUsb(USBSTATUS_SUCCESS); ++ } else { ++ fileToDump.delete(); + } - dumpFile(fileToDump, fileSize); -@@ -251,6 +310,49 @@ class NxdtUsbAbi1 { - - } ++ } ++ ++ private void handleCancelFileTransfer() throws Exception{ ++ resetNspInfo(); + writeUsb(USBSTATUS_SUCCESS); ++ logPrinter.print("User cancelled ongoing file transfer.", EMsgType.FAIL); ++ } +- if (fileSize == 0) + private void handleSendNspHeader(byte[] message) throws Exception{ + final int headerSize = getLEint(message, 0x8); + @@ -194,9 +248,10 @@ index dd2a1bc..58add89 100644 + writeUsb(USBSTATUS_MALFORMED_REQUEST); + logPrinter.print("Received NSP send header request outside of NSP transfer mode!", EMsgType.FAIL); + resetNspInfo(); -+ return; + return; + } -+ + +- dumpFile(fileToDump, fileSize); + if (this.nspRemainingSize > 0) { + writeUsb(USBSTATUS_MALFORMED_REQUEST); + logPrinter.print("Received NSP send header request without receiving all NSP file entry data!", EMsgType.FAIL); @@ -218,54 +273,225 @@ index dd2a1bc..58add89 100644 + } + + resetNspInfo(); -+ -+ writeUsb(USBSTATUS_SUCCESS); + + 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); + private int getLEint(byte[] bytes, int fromOffset){ +@@ -277,9 +366,9 @@ class NxdtUsbAbi1 { + throw new Exception("Unable to create dir(s) for file in "+folderForTheFile); + } + +- // @see https://bugs.openjdk.java.net/browse/JDK-8146538 +- private void dumpFile(File file, long size) throws Exception{ ++ private boolean dumpFile(File file, long size) throws Exception{ + FileOutputStream fos = new FileOutputStream(file, true); ++ boolean success = true; try (BufferedOutputStream bos = new BufferedOutputStream(fos)) { FileDescriptor fd = fos.getFD(); -@@ -296,15 +398,21 @@ class NxdtUsbAbi1 { +@@ -287,31 +376,44 @@ class NxdtUsbAbi1 { + long received = 0; + int bufferSize; + +- while (received+NXDT_FILE_CHUNK_SIZE < size) { +- //readBuffer = readUsbFile(); +- readBuffer = readUsbFileDebug(NXDT_FILE_CHUNK_SIZE); ++ while((received + NXDT_FILE_CHUNK_SIZE) < size) { ++ readBuffer = readUsb(NXDT_FILE_CHUNK_SIZE, NXDT_USB_DATA_TIMEOUT); + bos.write(readBuffer); + if (isWindows10) + fd.sync(); bufferSize = readBuffer.length; received += bufferSize; - logPrinter.updateProgress((double)received / (double)size); ++ if (bufferSize == 0x10 && Arrays.equals(Arrays.copyOfRange(readBuffer, 0, 4), MAGIC_NXDT)) { ++ int cmd = getLEint(readBuffer, 4); ++ if (cmd == CMD_CANCEL_FILE_TRANSFER){ ++ handleCancelFileTransfer(); ++ success = false; ++ break; ++ } ++ } ++ + if (!this.nspTransferMode) { + logPrinter.updateProgress((double)received / (double)size); + } else { + this.nspRemainingSize -= bufferSize; + logPrinter.updateProgress((double)(this.nspSize - this.nspRemainingSize) / (double)this.nspSize); + } ++ } ++ if (success){ ++ int lastChunkSize = (int)((size - received) + 1); ++ readBuffer = readUsb(lastChunkSize, NXDT_USB_DATA_TIMEOUT); ++ bos.write(readBuffer); ++ if (isWindows10) ++ fd.sync(); ++ this.nspRemainingSize -= lastChunkSize; } - int lastChunkSize = (int)(size - received) + 1; - readBuffer = readUsbFileDebug(lastChunkSize); - bos.write(readBuffer); - if (isWindows10) - fd.sync(); -+ if (this.nspTransferMode) this.nspRemainingSize -= (lastChunkSize - 1); +- int lastChunkSize = (int)(size - received) + 1; +- readBuffer = readUsbFileDebug(lastChunkSize); +- bos.write(readBuffer); +- if (isWindows10) +- fd.sync(); } finally { - logPrinter.updateProgress(1.0); -+ if (!this.nspTransferMode || (this.nspTransferMode && this.nspRemainingSize == 0)) logPrinter.updateProgress(1.0); ++ if (success && (!this.nspTransferMode || (this.nspTransferMode && this.nspRemainingSize == 0))) logPrinter.updateProgress(1.0); } ++ ++ return success; } - /* Handle Zero-length terminator +- /* Handle Zero-length terminator +- private boolean isAligned(long size){ +- return ((size & (endpointMaxPacketSize - 1)) == 0); +- } +- */ + + /** Sending any byte array to USB device **/ + private void writeUsb(byte[] message) throws Exception{ +@@ -322,7 +424,7 @@ class NxdtUsbAbi1 { + if ( parent.isCancelled() ) + throw new InterruptedException("Execution interrupted"); + +- int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_TIMEOUT); ++ int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x01, writeBuffer, writeBufTransferred, NXDT_USB_CMD_TIMEOUT); + + if (result == LibUsb.SUCCESS) { + if (writeBufTransferred.get() == message.length) +@@ -335,47 +437,61 @@ class NxdtUsbAbi1 { + "\n Returned: " + UsbErrorCodes.getErrCode(result) + + "\n (execution stopped)"); + } ++ + /** +- * Reading what USB device responded (command). ++ * Reads an USB directive. + * @return byte array if data read successful + * 'null' if read failed + * */ + private byte[] readUsbDirective() throws Exception{ +- ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_MAX_DIRECTIVE_SIZE); +- // 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 (! parent.isCancelled()) { +- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 ++ byte[] cmd_header = null, payload = null, directive = null; ++ int payloadSize = 0; + +- switch (result) { +- case LibUsb.SUCCESS: +- int trans = readBufTransferred.get(); +- byte[] receivedBytes = new byte[trans]; +- readBuffer.get(receivedBytes); +- return receivedBytes; +- case LibUsb.ERROR_TIMEOUT: +- break; +- default: +- throw new Exception("Data transfer issue [read command]" + +- "\n Returned: " + UsbErrorCodes.getErrCode(result)+ +- "\n (execution stopped)"); ++ cmd_header = readUsb(0x10, NXDT_USB_CMD_TIMEOUT); ++ if (cmd_header == null || cmd_header.length == 0) return null; ++ ++ if (cmd_header.length != 0x10){ ++ writeUsb(USBSTATUS_MALFORMED_REQUEST); ++ logPrinter.print("Command header is too small. Only "+cmd_header.length+" bytes received.", EMsgType.FAIL); ++ return null; ++ } ++ ++ if (! Arrays.equals(Arrays.copyOfRange(cmd_header, 0, 4), MAGIC_NXDT)){ ++ writeUsb(USBSTATUS_INVALID_MAGIC); ++ logPrinter.print("Invalid 'MAGIC'", EMsgType.FAIL); ++ return null; ++ } ++ ++ payloadSize = getLEint(cmd_header, 8); ++ if (payloadSize > 0){ ++ payload = readUsb(payloadSize + 1, NXDT_USB_CMD_TIMEOUT); ++ if (payload == null || payload.length != payloadSize){ ++ writeUsb(USBSTATUS_MALFORMED_REQUEST); ++ logPrinter.print("Command payload size mismatch. Received "+payload.length+" bytes.", EMsgType.FAIL); ++ return null; + } + } +- throw new InterruptedException(); ++ ++ ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); ++ outputStream.write(cmd_header); ++ if (payloadSize > 0) outputStream.write(payload); ++ directive = outputStream.toByteArray(); ++ ++ return directive; + } ++ + /** +- * Reading what USB device responded (file). ++ * Reading what USB device responded (command). + * @return byte array if data read successful + * 'null' if read failed + * */ +- private byte[] readUsbFile() throws Exception{ +- ByteBuffer readBuffer = ByteBuffer.allocateDirect(NXDT_FILE_CHUNK_SIZE); ++ private byte[] readUsb(int length, int timeout) throws Exception{ ++ ByteBuffer readBuffer = ByteBuffer.allocateDirect(alignUp(length, USB_BUF_ALIGNMENT)); + IntBuffer readBufTransferred = IntBuffer.allocate(1); + int result; +- int countDown = 0; +- while (! parent.isCancelled() && countDown < 5) { +- result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, 1000); ++ ++ while (! parent.isCancelled()) { ++ result = LibUsb.bulkTransfer(handlerNS, (byte)0x81, readBuffer, readBufTransferred, timeout); // last one is TIMEOUT. 0 stands for unlimited. Endpoint IN = 0x81 + + switch (result) { + case LibUsb.SUCCESS: +@@ -384,33 +500,17 @@ class NxdtUsbAbi1 { + readBuffer.get(receivedBytes); + return receivedBytes; + case LibUsb.ERROR_TIMEOUT: +- countDown++; + break; + default: +- throw new Exception("Data transfer issue [read file]" + ++ throw new Exception("Data transfer issue [read]" + + "\n Returned: " + UsbErrorCodes.getErrCode(result)+ + "\n (execution stopped)"); + } + } + throw new InterruptedException(); + } +- +- private byte[] readUsbFileDebug(int chunkSize) throws Exception { +- ByteBuffer readBuffer = ByteBuffer.allocateDirect(chunkSize); +- IntBuffer readBufTransferred = IntBuffer.allocate(1); +- if (parent.isCancelled()) +- throw new InterruptedException(); +- +- int result = LibUsb.bulkTransfer(handlerNS, (byte) 0x81, readBuffer, readBufTransferred, NXDT_USB_TIMEOUT); + +- if (result == LibUsb.SUCCESS) { +- int trans = readBufTransferred.get(); +- byte[] receivedBytes = new byte[trans]; +- readBuffer.get(receivedBytes); +- return receivedBytes; +- } +- throw new Exception("Data transfer issue [read file]" + +- "\n Returned: " + UsbErrorCodes.getErrCode(result) + +- "\n (execution stopped)"); ++ private int alignUp(int value, int alignment){ ++ return ((value + (alignment - 1)) & ~(alignment - 1)); + } + } diff --git a/src/main/resources/NSLMain.fxml b/src/main/resources/NSLMain.fxml index a2d42d6..9114c3d 100644 --- a/src/main/resources/NSLMain.fxml diff --git a/source/usb.c b/source/usb.c index 3d51966..2035fc2 100644 --- a/source/usb.c +++ b/source/usb.c @@ -657,7 +657,7 @@ NX_INLINE void usbPrepareCommandHeader(u32 cmd, u32 cmd_block_size) static bool usbSendCommand(void) { UsbCommandHeader *cmd_header = (UsbCommandHeader*)g_usbTransferBuffer; - size_t cmd_size = (sizeof(UsbCommandHeader) + cmd_header->cmd_block_size); + u32 cmd = cmd_header->cmd, cmd_block_size = cmd_header->cmd_block_size; UsbStatus *cmd_status = (UsbStatus*)g_usbTransferBuffer; u32 status = UsbStatusType_Success; @@ -665,7 +665,7 @@ static bool usbSendCommand(void) /* Log error message only if the USB session has been started, or if thread exit flag hasn't been enabled. */ bool ret = false, zlt_required = false, cmd_block_written = false, log_rw_errors = (g_usbSessionStarted || !g_usbDetectionThreadExitFlag); - if (cmd_size > USB_TRANSFER_BUFFER_SIZE) + if ((sizeof(UsbCommandHeader) + cmd_block_size) > USB_TRANSFER_BUFFER_SIZE) { LOGFILE("Invalid command size!"); status = UsbStatusType_InvalidCommandSize; @@ -675,26 +675,26 @@ static bool usbSendCommand(void) /* Write command header first. */ if (!usbWrite(cmd_header, sizeof(UsbCommandHeader))) { - if (log_rw_errors) LOGFILE("Failed to write header for type 0x%X command!", cmd_header->cmd); + if (log_rw_errors) LOGFILE("Failed to write header for type 0x%X command!", cmd); status = UsbStatusType_WriteCommandFailed; goto end; } /* Check if we need to transfer a command block. */ - if (cmd_header->cmd_block_size) + if (cmd_block_size) { + /* Move command block data within the transfer buffer to guarantee we'll work with proper alignment. */ + memmove(g_usbTransferBuffer, g_usbTransferBuffer + sizeof(UsbCommandHeader), cmd_block_size); + /* Determine if we'll need to set a Zero Length Termination (ZLT) packet after sending the command block. */ - zlt_required = (g_usbSessionStarted && IS_ALIGNED(cmd_header->cmd_block_size, g_usbEndpointMaxPacketSize)); + zlt_required = IS_ALIGNED(cmd_block_size, g_usbEndpointMaxPacketSize); if (zlt_required) usbSetZltPacket(true); - /* Move command block data within the transfer buffer to guarantee we'll work with proper alignment. */ - memmove(g_usbTransferBuffer, g_usbTransferBuffer + sizeof(UsbCommandHeader), cmd_header->cmd_block_size); - /* Write command block. */ - cmd_block_written = usbWrite(g_usbTransferBuffer, cmd_header->cmd_block_size); + cmd_block_written = usbWrite(g_usbTransferBuffer, cmd_block_size); if (!cmd_block_written) { - if (log_rw_errors) LOGFILE("Failed to write command block for type 0x%X command!", cmd_header->cmd); + if (log_rw_errors) LOGFILE("Failed to write command block for type 0x%X command!", cmd); status = UsbStatusType_WriteCommandFailed; } @@ -708,7 +708,7 @@ static bool usbSendCommand(void) /* Read status block. */ if (!usbRead(cmd_status, sizeof(UsbStatus))) { - if (log_rw_errors) LOGFILE("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd_header->cmd); + if (log_rw_errors) LOGFILE("Failed to read 0x%lX bytes long status block for type 0x%X command!", sizeof(UsbStatus), cmd); status = UsbStatusType_ReadStatusFailed; goto end; }