diff --git a/host/nxdt_host.py b/host/nxdt_host.py index 88ad833..bffeb8a 100644 --- a/host/nxdt_host.py +++ b/host/nxdt_host.py @@ -331,6 +331,7 @@ g_taskbar: Any = None g_usbEpIn: Any = None g_usbEpOut: Any = None g_usbEpMaxPacketSize: int = 0 +g_usbVersion: str = '' g_nxdtVersionMajor: int = 0 g_nxdtVersionMinor: int = 0 @@ -634,7 +635,7 @@ def utilsGetSizeUnitAndDivisor(size: int) -> tuple[str, int]: return ret def usbGetDeviceEndpoints() -> bool: - global g_usbEpIn, g_usbEpOut, g_usbEpMaxPacketSize + global g_usbEpIn, g_usbEpOut, g_usbEpMaxPacketSize, g_usbVersion assert g_logger is not None @@ -642,7 +643,6 @@ def usbGetDeviceEndpoints() -> bool: prev_dev: usb.core.Device | None = None usb_ep_in_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_IN usb_ep_out_lambda = lambda ep: usb.util.endpoint_direction(ep.bEndpointAddress) == usb.util.ENDPOINT_OUT - usb_version = 0 if g_cliMode: g_logger.info(SERVER_START_MSG) @@ -706,12 +706,12 @@ def usbGetDeviceEndpoints() -> bool: # Save endpoint max packet size and USB version. g_usbEpMaxPacketSize = g_usbEpIn.wMaxPacketSize - usb_version = cur_dev.bcdUSB + g_usbVersion = f'{cur_dev.bcdUSB >> 8}.{(cur_dev.bcdUSB & 0xFF) >> 4}' break g_logger.debug(f'Successfully retrieved USB endpoints! (bus {cur_dev.bus}, address {cur_dev.address}).') - g_logger.debug(f'Max packet size: 0x{g_usbEpMaxPacketSize:X} (USB {usb_version >> 8}.{(usb_version & 0xFF) >> 4}).\n') + g_logger.debug(f'Max packet size: 0x{g_usbEpMaxPacketSize:X}. BCD USB: 0x{cur_dev.bcdUSB:04X}.\n') if g_cliMode: g_logger.info(SERVER_STOP_MSG) @@ -770,7 +770,7 @@ def usbHandleStartSession(cmd_block: bytes) -> int: g_nxdtAbiVersionMinor = (abi_version & 0x0F) # Print client info. - g_logger.info(f'Client info: {USB_DEV_PRODUCT} v{g_nxdtVersionMajor}.{g_nxdtVersionMinor}.{g_nxdtVersionMicro}, USB ABI v{g_nxdtAbiVersionMajor}.{g_nxdtAbiVersionMinor} (commit {g_nxdtGitCommit}).\n') + g_logger.info(f'Client info: {USB_DEV_PRODUCT} v{g_nxdtVersionMajor}.{g_nxdtVersionMinor}.{g_nxdtVersionMicro}, USB ABI v{g_nxdtAbiVersionMajor}.{g_nxdtAbiVersionMinor} (commit {g_nxdtGitCommit}), USB {g_usbVersion}.\n') # Check if we support this ABI version. if (g_nxdtAbiVersionMajor != USB_ABI_VERSION_MAJOR) or (g_nxdtAbiVersionMinor != USB_ABI_VERSION_MINOR): @@ -792,8 +792,9 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None: g_logger.debug(f'Received SendFileProperties ({USB_CMD_SEND_FILE_PROPERTIES:02X}) command.') # Parse command block. - (file_size, filename_length, nsp_header_size, raw_filename) = struct.unpack_from(f' int | None: # Check if we're dealing with a CancelFileTransfer command. if chunk_size == USB_CMD_HEADER_SIZE: - (magic, cmd_id, _) = struct.unpack_from('<4sII', chunk, 0) - if (magic == USB_MAGIC_WORD) and (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER): + (magic, cmd_id, cmd_block_size, _) = struct.unpack_from('<4sIII', chunk, 0) + if (magic == USB_MAGIC_WORD) and (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER) and (cmd_block_size == 0): # Cancel file transfer. cancelTransfer() - g_logger.debug(f'Received CancelFileTransfer ({USB_CMD_CANCEL_FILE_TRANSFER:02X}) command.') + g_logger.debug(f'Received CancelFileTransfer ({USB_CMD_CANCEL_FILE_TRANSFER:02X}) command:\n{bytes.hex(chunk, " ", 1)}\n') g_logger.warning('Transfer cancelled.') # Let the command handler take care of sending the status response for us. @@ -998,7 +999,7 @@ def usbHandleSendFileProperties(cmd_block: bytes) -> int | None: def usbHandleCancelFileTransfer(cmd_block: bytes) -> int: assert g_logger is not None - g_logger.debug(f'Received CancelFileTransfer ({USB_CMD_START_SESSION:02X}) command.') + g_logger.debug(f'Received CancelFileTransfer ({USB_CMD_CANCEL_FILE_TRANSFER:02X}) command.') if g_nspTransferMode: if (g_nspSize > USB_TRANSFER_THRESHOLD) and (g_progressBarWindow is not None): @@ -1112,8 +1113,10 @@ def usbCommandHandler() -> None: g_logger.error(f'Failed to read 0x{USB_CMD_HEADER_SIZE:X}-byte long command header!') break + g_logger.debug(f'Received command header data:\n{bytes.hex(cmd_header, " ", 1)}\n') + # Parse command header. - (magic, cmd_id, cmd_block_size) = struct.unpack_from('<4sII', cmd_header, 0) + (magic, cmd_id, cmd_block_size, _) = struct.unpack_from('<4sIII', cmd_header, 0) # Read command block right away (if needed). # nxdumptool expects us to read it right after sending the command header. @@ -1130,6 +1133,8 @@ def usbCommandHandler() -> None: g_logger.error(f'Failed to read 0x{cmd_block_size:X}-byte long command block for command ID {cmd_id:02X}!') break + g_logger.debug(f'Received command block data:\n{bytes.hex(cmd_block, " ", 1)}\n') + # Verify magic word. if magic != USB_MAGIC_WORD: g_logger.error('Received command header with invalid magic word!\n') @@ -1146,6 +1151,7 @@ def usbCommandHandler() -> None: # Verify command block size. if (cmd_id == USB_CMD_START_SESSION and cmd_block_size != USB_CMD_BLOCK_SIZE_START_SESSION) or \ (cmd_id == USB_CMD_SEND_FILE_PROPERTIES and cmd_block_size != USB_CMD_BLOCK_SIZE_SEND_FILE_PROPERTIES) or \ + (cmd_id == USB_CMD_CANCEL_FILE_TRANSFER and cmd_block_size) or \ (cmd_id == USB_CMD_SEND_NSP_HEADER and not cmd_block_size) or \ (cmd_id == USB_CMD_START_EXTRACTED_FS_DUMP and cmd_block_size != USB_CMD_BLOCK_SIZE_START_EXTRACTED_FS_DUMP): g_logger.error(f'Invalid command block size for command ID {cmd_id:02X}! (0x{cmd_block_size:X}).\n') diff --git a/source/core/bis_storage.c b/source/core/bis_storage.c index c617e75..c330140 100644 --- a/source/core/bis_storage.c +++ b/source/core/bis_storage.c @@ -181,7 +181,7 @@ static bool bisStorageMountPartition(u8 bis_partition_id) BisStorageFatFsContext *bis_fatfs_ctx = NULL; Result rc = 0; FRESULT fr = FR_OK; - bool success = false; + bool success = false, empty_partition = false; /* Check if we have already mounted this eMMC partition. */ if (BIS_STORAGE_FATFS_CTX(bis_partition_id)) @@ -222,6 +222,11 @@ static bool bisStorageMountPartition(u8 bis_partition_id) if (fr != FR_OK) { LOG_MSG_ERROR("Failed to mount %s partition via FatFs! (%u).", bis_fatfs_ctx->gpt_name, fr); + + /* Add an exception for partitions that don't hold a valid FAT volume. */ + /* TODO: find out why some of these partitions are empty in some consoles. */ + if (fr == FR_NO_FILESYSTEM) empty_partition = true; + goto end; } @@ -240,6 +245,7 @@ end: { bisStorageFreeFatFsContext(&bis_fatfs_ctx); BIS_STORAGE_FATFS_CTX(bis_partition_id) = NULL; + if (empty_partition) success = true; } return success; diff --git a/source/core/nxdt_utils.c b/source/core/nxdt_utils.c index fed6c49..bc0fbdd 100644 --- a/source/core/nxdt_utils.c +++ b/source/core/nxdt_utils.c @@ -1066,35 +1066,52 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex /* Get current path element size. */ size_t element_size = (ptr2 ? (size_t)(ptr2 - ptr1) : (path_len - (size_t)(ptr1 - path))); - /* Get UTF-8 string limit. */ - /* Use our max filename length as the byte count limit. */ - size_t last_cp_pos = utilsGetUtf8StringLimit(ptr1, element_size, max_filename_len); - if (last_cp_pos < element_size) + /* Short-circuit: proceed onto the next path element right away if the current one fits within our max filename length. */ + if (element_size <= max_filename_len) { - if (ptr2) - { - /* Truncate current element by moving the rest of the path to the current position. */ - memmove(ptr1 + last_cp_pos, ptr2, path_len - (size_t)(ptr2 - path)); - - /* Update pointer. */ - ptr2 -= (element_size - last_cp_pos); - } else - if (use_extension) - { - /* Truncate last element. Make sure to preserve the provided file extension. */ - if (extension_len >= last_cp_pos) - { - LOG_MSG_ERROR("File extension length is >= truncated filename length! (0x%lX >= 0x%lX).", extension_len, last_cp_pos); - goto end; - } - - memmove(ptr1 + last_cp_pos - extension_len, ptr1 + element_size - extension_len, extension_len); - } - - path_len -= (element_size - last_cp_pos); - path[path_len] = '\0'; + /* Update pointer. */ + ptr1 = ptr2; + continue; } + /* Get UTF-8 string limit. */ + /* We'll use our max filename length as the byte count limit. */ + /* Make sure to preserve the file extension if it was provided and if we're dealing with the last path element. */ + size_t byte_limit = ((ptr2 || !use_extension) ? max_filename_len : (max_filename_len - extension_len)); + + size_t last_cp_pos = utilsGetUtf8StringLimit(ptr1, element_size, byte_limit); + if (last_cp_pos > byte_limit) + { + /* Something went terribly wrong somewhere. */ + LOG_MSG_ERROR("Unable to appropiately determine last UTF-8 codepoint position for path element \"%.*s\" in \"%s\" (%lu >= %lu).", (int)element_size, ptr1, path, last_cp_pos, byte_limit); + goto end; + } + + /* Prepare variables for path truncation. */ + char *ptr3 = NULL; + size_t move_size = 0, diff = (element_size - last_cp_pos); + + if (ptr2) + { + ptr3 = ptr2; + move_size = (path_len - (size_t)(ptr2 - path)); + ptr2 = (ptr1 + last_cp_pos); + } else + if (use_extension) + { + ptr3 = (ptr1 + element_size - extension_len); + move_size = extension_len; + diff -= extension_len; + } + + /* Truncate path element by moving the rest of the path string to the last UTF-8 codepoint position, if needed. */ + if (ptr3 && move_size) memmove(ptr1 + last_cp_pos, ptr3, move_size); + + /* Update path length. */ + path_len -= diff; + path[path_len] = '\0'; + + /* Update pointer. */ ptr1 = ptr2; } @@ -1546,24 +1563,30 @@ static size_t utilsGetUtf8StringLimit(const char *str, size_t str_size, size_t b { if (!str || !*str || !str_size || !byte_limit) return 0; + /* Short-circuit: return immediately if we have enough space to hold the full string. */ if (byte_limit > str_size) return str_size; u32 code = 0; ssize_t units = 0; - size_t cur_pos = 0, last_cp_pos = 0; - const u8 *str_u8 = (const u8*)str; + size_t cur_pos = 0; + const u8 *ptr = (const u8*)str; - while(cur_pos < str_size && cur_pos < byte_limit) - { - units = decode_utf8(&code, str_u8 + cur_pos); + do { + /* Decode current codepoint. */ + units = decode_utf8(&code, ptr); + if (units < 0) break; + + /* Calculate new position within the input string. */ + /* Bail out immediately if we have exceeded a size limitation. */ size_t new_pos = (cur_pos + (size_t)units); - if (units < 0 || !code || new_pos > str_size) break; + if (new_pos > str_size || new_pos > byte_limit) break; + /* Update current position. */ cur_pos = new_pos; - if (cur_pos < byte_limit) last_cp_pos = cur_pos; - } + ptr += units; + } while(code != 0); - return last_cp_pos; + return cur_pos; } static char utilsConvertHexDigitToBinary(char c)