1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-29 13:42:11 +00:00

Some changes.

* nxdt_includes: renamed VersionType1 to Version and tweaked it to add the application_version struct, renamed VersionType2 to SdkAddOnVersion.

* gc_dumper: use utilsGeneratePath() to truncate generated filenames to safe filesystem limits. Thanks to n0tw0rk for reporting the issue and testing the changes!

* nxdt_host.py: fixed a string formatting issue that made it impossible to unpack data from the SendFileProperties command block under certain circumstances.

* gamecard: modified GameCardKeySource to add a value field, added GameCardFlags_HasCa10Certificate flag.

* utils: utilsGeneratePath() no longer adds a dot on its own if it's not part of the provided extension string.
This commit is contained in:
Pablo Curiel 2022-03-17 13:37:24 +01:00
parent b9018a7df0
commit 278cb5c72a
14 changed files with 149 additions and 106 deletions

View file

@ -80,6 +80,8 @@ static bool waitForUsb(void);
static void generateDumpTxt(void); static void generateDumpTxt(void);
static bool saveDumpTxt(void); static bool saveDumpTxt(void);
static char *generateOutputFileName(const char *extension);
static bool saveGameCardSpecificData(void); static bool saveGameCardSpecificData(void);
static bool saveGameCardCertificate(void); static bool saveGameCardCertificate(void);
static bool saveGameCardInitialData(void); static bool saveGameCardInitialData(void);
@ -557,6 +559,29 @@ static bool saveDumpTxt(void)
return saveFileData(path, txt_info, strlen(txt_info)); return saveFileData(path, txt_info, strlen(txt_info));
} }
static char *generateOutputFileName(const char *extension)
{
char *filename = NULL, *output = NULL;
if (!extension || !*extension || !(filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)))
{
consolePrint("failed to get gamecard filename!\n");
return NULL;
}
output = utilsGeneratePath(NULL, filename, extension);
free(filename);
if (output)
{
snprintf(path, MAX_ELEMENTS(path), "%s", output);
} else {
consolePrint("failed to generate output filename!\n");
}
return output;
}
static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out) static bool dumpGameCardSecurityInformation(GameCardSecurityInformation *out)
{ {
if (!out) if (!out)
@ -580,16 +605,19 @@ static bool saveGameCardSpecificData(void)
GameCardSecurityInformation gc_security_information = {0}; GameCardSecurityInformation gc_security_information = {0};
bool success = false; bool success = false;
u32 crc = 0; u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); char *filename = NULL;
if (!dumpGameCardSecurityInformation(&gc_security_information) || !filename) goto end; if (!dumpGameCardSecurityInformation(&gc_security_information)) goto end;
crc = crc32Calculate(&(gc_security_information.specific_data), sizeof(GameCardSpecificData)); crc = crc32Calculate(&(gc_security_information.specific_data), sizeof(GameCardSpecificData));
snprintf(path, MAX_ELEMENTS(path), "%s (Specific Data) (%08X).bin", filename, crc); snprintf(path, MAX_ELEMENTS(path), " (Specific Data) (%08X).bin", crc);
if (!saveFileData(path, &(gc_security_information.specific_data), sizeof(GameCardSpecificData))) goto end; filename = generateOutputFileName(path);
if (!filename) goto end;
printf("successfully saved specific data as \"%s\"\n", path); if (!saveFileData(filename, &(gc_security_information.specific_data), sizeof(GameCardSpecificData))) goto end;
printf("successfully saved specific data as \"%s\"\n", filename);
success = true; success = true;
end: end:
@ -603,9 +631,9 @@ static bool saveGameCardCertificate(void)
FsGameCardCertificate gc_cert = {0}; FsGameCardCertificate gc_cert = {0};
bool success = false; bool success = false;
u32 crc = 0; u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); char *filename = NULL;
if (!gamecardGetCertificate(&gc_cert) || !filename) if (!gamecardGetCertificate(&gc_cert))
{ {
consolePrint("failed to get gamecard certificate\n"); consolePrint("failed to get gamecard certificate\n");
goto end; goto end;
@ -614,11 +642,14 @@ static bool saveGameCardCertificate(void)
consolePrint("get gamecard certificate ok\n"); consolePrint("get gamecard certificate ok\n");
crc = crc32Calculate(&gc_cert, sizeof(FsGameCardCertificate)); crc = crc32Calculate(&gc_cert, sizeof(FsGameCardCertificate));
snprintf(path, MAX_ELEMENTS(path), "%s (Certificate) (%08X).bin", filename, crc); snprintf(path, MAX_ELEMENTS(path), " (Certificate) (%08X).bin", crc);
if (!saveFileData(path, &gc_cert, sizeof(FsGameCardCertificate))) goto end; filename = generateOutputFileName(path);
if (!filename) goto end;
printf("successfully saved certificate as \"%s\"\n", path); if (!saveFileData(filename, &gc_cert, sizeof(FsGameCardCertificate))) goto end;
printf("successfully saved certificate as \"%s\"\n", filename);
success = true; success = true;
end: end:
@ -632,16 +663,19 @@ static bool saveGameCardInitialData(void)
GameCardSecurityInformation gc_security_information = {0}; GameCardSecurityInformation gc_security_information = {0};
bool success = false; bool success = false;
u32 crc = 0; u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); char *filename = NULL;
if (!dumpGameCardSecurityInformation(&gc_security_information) || !filename) goto end; if (!dumpGameCardSecurityInformation(&gc_security_information)) goto end;
crc = crc32Calculate(&(gc_security_information.initial_data), sizeof(GameCardInitialData)); crc = crc32Calculate(&(gc_security_information.initial_data), sizeof(GameCardInitialData));
snprintf(path, MAX_ELEMENTS(path), "%s (Initial Data) (%08X).bin", filename, crc); snprintf(path, MAX_ELEMENTS(path), " (Initial Data) (%08X).bin", crc);
if (!saveFileData(path, &(gc_security_information.initial_data), sizeof(GameCardInitialData))) goto end; filename = generateOutputFileName(path);
if (!filename) goto end;
printf("successfully saved initial data as \"%s\"\n", path); if (!saveFileData(filename, &(gc_security_information.initial_data), sizeof(GameCardInitialData))) goto end;
printf("successfully saved initial data as \"%s\"\n", filename);
success = true; success = true;
end: end:
@ -655,16 +689,19 @@ static bool saveGameCardIdSet(void)
FsGameCardIdSet id_set = {0}; FsGameCardIdSet id_set = {0};
bool success = false; bool success = false;
u32 crc = 0; u32 crc = 0;
char *filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); char *filename = NULL;
if (!gamecardGetIdSet(&id_set) || !filename) goto end; if (!gamecardGetIdSet(&id_set)) goto end;
crc = crc32Calculate(&id_set, sizeof(FsGameCardIdSet)); crc = crc32Calculate(&id_set, sizeof(FsGameCardIdSet));
snprintf(path, MAX_ELEMENTS(path), "%s (Card ID Set) (%08X).bin", filename, crc); snprintf(path, MAX_ELEMENTS(path), " (Card ID Set) (%08X).bin", crc);
if (!saveFileData(path, &id_set, sizeof(FsGameCardIdSet))) goto end; if (!saveFileData(filename, &id_set, sizeof(FsGameCardIdSet))) goto end;
printf("successfully saved gamecard id set as \"%s\"\n", path); filename = generateOutputFileName(path);
if (!filename) goto end;
printf("successfully saved gamecard id set as \"%s\"\n", filename);
success = true; success = true;
end: end:
@ -686,12 +723,9 @@ static bool saveGameCardImage(void)
consolePrint("gamecard image dump\nkeep certificate: %s | trim dump: %s | calculate crc32: %s\n\n", g_keepCertificate ? "yes" : "no", g_trimDump ? "yes" : "no", g_calcCrc ? "yes" : "no"); consolePrint("gamecard image dump\nkeep certificate: %s | trim dump: %s | calculate crc32: %s\n\n", g_keepCertificate ? "yes" : "no", g_trimDump ? "yes" : "no", g_calcCrc ? "yes" : "no");
filename = titleGenerateGameCardFileName(TitleNamingConvention_Full, g_useUsbHost ? TitleFileNameIllegalCharReplaceType_IllegalFsChars : TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); snprintf(path, MAX_ELEMENTS(path), " (%s) (%s).xci", g_keepCertificate ? "cert" : "certless", g_trimDump ? "trimmed" : "untrimmed");
if (!filename) filename = generateOutputFileName(path);
{ if (!filename) goto end;
consolePrint("failed to generate gamecard filename!\n");
goto end;
}
shared_data.data = usbAllocatePageAlignedBuffer(BLOCK_SIZE); shared_data.data = usbAllocatePageAlignedBuffer(BLOCK_SIZE);
if (!shared_data.data) if (!shared_data.data)
@ -710,25 +744,23 @@ static bool saveGameCardImage(void)
consolePrint("gamecard size: 0x%lX\n", gc_size); consolePrint("gamecard size: 0x%lX\n", gc_size);
snprintf(path, MAX_ELEMENTS(path), "%s (%s) (%s).xci", filename, g_keepCertificate ? "cert" : "certless", g_trimDump ? "trimmed" : "untrimmed"); if (g_useUsbHost && !usbSendFilePropertiesCommon(gc_size, filename))
if (g_useUsbHost && !usbSendFilePropertiesCommon(gc_size, path))
{ {
consolePrint("failed to send file properties for \"%s\"!\n", path); consolePrint("failed to send file properties for \"%s\"!\n", filename);
goto end; goto end;
} else } else
if (!g_useUsbHost) if (!g_useUsbHost)
{ {
if (gc_size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(path)) if (gc_size > FAT32_FILESIZE_LIMIT && !utilsCreateConcatenationFile(filename))
{ {
consolePrint("failed to create concatenation file for \"%s\"!\n", path); consolePrint("failed to create concatenation file for \"%s\"!\n", filename);
goto end; goto end;
} }
shared_data.fp = fopen(path, "wb"); shared_data.fp = fopen(filename, "wb");
if (!shared_data.fp) if (!shared_data.fp)
{ {
consolePrint("failed to open \"%s\" for writing!\n", path); consolePrint("failed to open \"%s\" for writing!\n", filename);
goto end; goto end;
} }
} }
@ -824,7 +856,7 @@ end:
shared_data.fp = NULL; shared_data.fp = NULL;
} }
if (!success && !g_useUsbHost) utilsRemoveConcatenationFile(path); if (!success && !g_useUsbHost) utilsRemoveConcatenationFile(filename);
if (shared_data.data) free(shared_data.data); if (shared_data.data) free(shared_data.data);

View file

@ -123,8 +123,8 @@ static void nspDump(TitleInfo *title_info, u64 free_space)
printf("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); 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("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, \ printf("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \
title_info->version.minor_relstep); title_info->version.system_version.minor_relstep);
printf("content count: %u\n", title_info->content_count); printf("content count: %u\n", title_info->content_count);
printf("size: %s\n", title_info->size_str); printf("size: %s\n", title_info->size_str);
printf("______________________________\n\n"); printf("______________________________\n\n");
@ -895,8 +895,8 @@ int main(int argc, char *argv[])
(title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); (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")); 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); 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, \ printf("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \
title_info->version.minor_relstep); title_info->version.system_version.minor_relstep);
printf("content count: %u\n", title_info->content_count); printf("content count: %u\n", title_info->content_count);
printf("size: %s\n", title_info->size_str); printf("size: %s\n", title_info->size_str);
} }

View file

@ -859,8 +859,8 @@ static void nspDump(TitleInfo *title_info)
consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); 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("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, \ consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \
title_info->version.minor_relstep); title_info->version.system_version.minor_relstep);
consolePrint("content count: %u\n", title_info->content_count); consolePrint("content count: %u\n", title_info->content_count);
consolePrint("size: %s\n", title_info->size_str); consolePrint("size: %s\n", title_info->size_str);
consolePrint("______________________________\n\n"); consolePrint("______________________________\n\n");
@ -1046,8 +1046,8 @@ int main(int argc, char *argv[])
(title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc")); (title_info->meta_key.type == NcmContentMetaType_Patch ? "update" : "dlc"));
consolePrint("source storage: %s\n", title_info->storage_id == NcmStorageId_GameCard ? "gamecard" : (title_info->storage_id == NcmStorageId_BuiltInUser ? "emmc" : "sd card")); 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); 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, \ consolePrint("version: %u (%u.%u.%u-%u.%u)\n", title_info->version.value, title_info->version.system_version.major, title_info->version.system_version.minor, title_info->version.system_version.micro, title_info->version.system_version.major_relstep, \
title_info->version.minor_relstep); title_info->version.system_version.minor_relstep);
consolePrint("content count: %u\n", title_info->content_count); consolePrint("content count: %u\n", title_info->content_count);
consolePrint("size: %s\n", title_info->size_str); consolePrint("size: %s\n", title_info->size_str);
} }

View file

@ -629,7 +629,7 @@ def usbHandleSendFileProperties(cmd_block):
g_logger.debug('Received SendFileProperties (%02X) command.' % (USB_CMD_SEND_FILE_PROPERTIES)) g_logger.debug('Received SendFileProperties (%02X) command.' % (USB_CMD_SEND_FILE_PROPERTIES))
# Parse command block. # Parse command block.
(file_size, filename_length, nsp_header_size, raw_filename) = struct.unpack_from('<QII{}s'.format(USB_FILE_PROPERTIES_MAX_NAME_LENGTH), cmd_block, 0) (file_size, filename_length, nsp_header_size, raw_filename) = struct.unpack_from('<QII%ds' % (USB_FILE_PROPERTIES_MAX_NAME_LENGTH), cmd_block, 0)
filename = raw_filename.decode('utf-8').strip('\x00') filename = raw_filename.decode('utf-8').strip('\x00')
# Print info. # Print info.

View file

@ -51,7 +51,7 @@ typedef enum {
/// Finally, a 0x20 byte long digest is appended to the EOF. /// Finally, a 0x20 byte long digest is appended to the EOF.
typedef struct { typedef struct {
u64 title_id; u64 title_id;
VersionType1 version; Version version;
u8 content_meta_type; ///< NcmContentMetaType. u8 content_meta_type; ///< NcmContentMetaType.
u8 reserved_1; u8 reserved_1;
u16 extended_header_size; ///< Must match the size from the extended header struct for this content meta type (SystemUpdate, Application, Patch, AddOnContent, Delta). u16 extended_header_size; ///< Must match the size from the extended header struct for this content meta type (SystemUpdate, Application, Patch, AddOnContent, Delta).
@ -61,7 +61,7 @@ typedef struct {
u8 storage_id; ///< NcmStorageId. u8 storage_id; ///< NcmStorageId.
u8 content_install_type; ///< NcmContentInstallType. u8 content_install_type; ///< NcmContentInstallType.
u8 install_state; ///< ContentMetaInstallState. u8 install_state; ///< ContentMetaInstallState.
VersionType1 required_download_system_version; Version required_download_system_version;
u8 reserved_2[0x4]; u8 reserved_2[0x4];
} ContentMetaPackagedContentMetaHeader; } ContentMetaPackagedContentMetaHeader;
@ -76,20 +76,20 @@ typedef struct {
NXDT_ASSERT(ContentMetaSystemUpdateMetaExtendedHeader, 0x4); NXDT_ASSERT(ContentMetaSystemUpdateMetaExtendedHeader, 0x4);
/// Extended header for Application titles. /// Extended header for Application titles.
/// Equivalent to NcmApplicationMetaExtendedHeader, but using VersionType1 structs. /// Equivalent to NcmApplicationMetaExtendedHeader, but using Version structs.
typedef struct { typedef struct {
u64 patch_id; u64 patch_id;
VersionType1 required_system_version; Version required_system_version;
VersionType1 required_application_version; Version required_application_version;
} ContentMetaApplicationMetaExtendedHeader; } ContentMetaApplicationMetaExtendedHeader;
NXDT_ASSERT(ContentMetaApplicationMetaExtendedHeader, 0x10); NXDT_ASSERT(ContentMetaApplicationMetaExtendedHeader, 0x10);
/// Extended header for Patch titles. /// Extended header for Patch titles.
/// Equivalent to NcmPatchMetaExtendedHeader, but using a VersionType1 struct. /// Equivalent to NcmPatchMetaExtendedHeader, but using a Version struct.
typedef struct { typedef struct {
u64 application_id; u64 application_id;
VersionType1 required_system_version; Version required_system_version;
u32 extended_data_size; u32 extended_data_size;
u8 reserved[0x8]; u8 reserved[0x8];
} ContentMetaPatchMetaExtendedHeader; } ContentMetaPatchMetaExtendedHeader;
@ -97,10 +97,10 @@ typedef struct {
NXDT_ASSERT(ContentMetaPatchMetaExtendedHeader, 0x18); NXDT_ASSERT(ContentMetaPatchMetaExtendedHeader, 0x18);
/// Extended header for AddOnContent titles. /// Extended header for AddOnContent titles.
/// Equivalent to NcmAddOnContentMetaExtendedHeader, but using a VersionType1 struct. /// Equivalent to NcmAddOnContentMetaExtendedHeader, but using a Version struct.
typedef struct { typedef struct {
u64 application_id; u64 application_id;
VersionType1 required_application_version; Version required_application_version;
u8 reserved[0x4]; u8 reserved[0x4];
} ContentMetaAddOnContentMetaExtendedHeader; } ContentMetaAddOnContentMetaExtendedHeader;
@ -186,8 +186,8 @@ NXDT_ASSERT(ContentMetaPatchHistoryHeader, 0x38);
typedef struct { typedef struct {
u64 source_patch_id; u64 source_patch_id;
u64 destination_patch_id; u64 destination_patch_id;
VersionType1 source_version; Version source_version;
VersionType1 destination_version; Version destination_version;
u64 download_size; u64 download_size;
u8 reserved[0x8]; u8 reserved[0x8];
} ContentMetaPatchDeltaHistory; } ContentMetaPatchDeltaHistory;
@ -197,8 +197,8 @@ NXDT_ASSERT(ContentMetaPatchDeltaHistory, 0x28);
typedef struct { typedef struct {
u64 source_patch_id; u64 source_patch_id;
u64 destination_patch_id; u64 destination_patch_id;
VersionType1 source_version; Version source_version;
VersionType1 destination_version; Version destination_version;
u16 fragment_set_count; u16 fragment_set_count;
u8 reserved_1[0x6]; u8 reserved_1[0x6];
u16 content_info_count; u16 content_info_count;
@ -244,8 +244,8 @@ NXDT_ASSERT(ContentMetaFragmentIndicator, 0x4);
typedef struct { typedef struct {
u64 source_patch_id; u64 source_patch_id;
u64 destination_patch_id; u64 destination_patch_id;
VersionType1 source_version; Version source_version;
VersionType1 destination_version; Version destination_version;
u16 fragment_set_count; u16 fragment_set_count;
u8 reserved[0x6]; u8 reserved[0x6];
} ContentMetaDeltaMetaExtendedDataHeader; } ContentMetaDeltaMetaExtendedDataHeader;
@ -323,7 +323,7 @@ NX_INLINE u32 cnmtGetRequiredTitleVersion(ContentMetaContext *cnmt_ctx)
{ {
return ((cnmtIsValidContext(cnmt_ctx) && (cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Application || \ return ((cnmtIsValidContext(cnmt_ctx) && (cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Application || \
cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Patch || cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_AddOnContent)) ? \ cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_Patch || cnmt_ctx->packaged_header->content_meta_type == NcmContentMetaType_AddOnContent)) ? \
((VersionType1*)(cnmt_ctx->extended_header + sizeof(u64)))->value : 0); ((Version*)(cnmt_ctx->extended_header + sizeof(u64)))->value : 0);
} }
#ifdef __cplusplus #ifdef __cplusplus

View file

@ -56,8 +56,13 @@ NXDT_ASSERT(GameCardSpecificData, 0x200);
/// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware). /// Encrypted using AES-128-ECB with the common titlekek generator key (stored in the .rodata segment from the Lotus firmware).
typedef struct { typedef struct {
u64 package_id; ///< Matches package_id from GameCardHeader. union {
u8 reserved[0x8]; ///< Just zeroes. u8 value[0x10];
struct {
u64 package_id; ///< Matches package_id from GameCardHeader.
u8 reserved[0x8]; ///< Just zeroes.
};
};
} GameCardKeySource; } GameCardKeySource;
NXDT_ASSERT(GameCardKeySource, 0x10); NXDT_ASSERT(GameCardKeySource, 0x10);
@ -138,7 +143,8 @@ typedef enum {
GameCardFlags_HistoryErase = BIT(1), GameCardFlags_HistoryErase = BIT(1),
GameCardFlags_RepairTool = BIT(2), GameCardFlags_RepairTool = BIT(2),
GameCardFlags_DifferentRegionCupToTerraDevice = BIT(3), GameCardFlags_DifferentRegionCupToTerraDevice = BIT(3),
GameCardFlags_DifferentRegionCupToGlobalDevice = BIT(4) GameCardFlags_DifferentRegionCupToGlobalDevice = BIT(4),
GameCardFlags_HasCa10Certificate = BIT(7)
} GameCardFlags; } GameCardFlags;
typedef enum { typedef enum {
@ -172,18 +178,18 @@ typedef enum {
/// Production XCI header key hash: 2E36CC55157A351090A73E7AE77CF581F69B0B6E48FB066C984879A6ED7D2E96 /// Production XCI header key hash: 2E36CC55157A351090A73E7AE77CF581F69B0B6E48FB066C984879A6ED7D2E96
/// Development XCI header key hash: 61D5C02244188810E2E3DE69341AC0F3C7653D370C6D3F77CA82B0B7E59F39AD /// Development XCI header key hash: 61D5C02244188810E2E3DE69341AC0F3C7653D370C6D3F77CA82B0B7E59F39AD
typedef struct { typedef struct {
u64 fw_version; ///< GameCardFwVersion. u64 fw_version; ///< GameCardFwVersion.
u32 acc_ctrl_1; ///< GameCardAccCtrl1. u32 acc_ctrl_1; ///< GameCardAccCtrl1.
u32 wait_1_time_read; ///< Always 0x1388. u32 wait_1_time_read; ///< Always 0x1388.
u32 wait_2_time_read; ///< Always 0. u32 wait_2_time_read; ///< Always 0.
u32 wait_1_time_write; ///< Always 0. u32 wait_1_time_write; ///< Always 0.
u32 wait_2_time_write; ///< Always 0. u32 wait_2_time_write; ///< Always 0.
VersionType2 fw_mode; ///< Current SdkAddonVersion. SdkAddOnVersion fw_mode; ///< Current SdkAddOnVersion.
VersionType1 upp_version; ///< Bundled system update version. Version upp_version; ///< Bundled system update version.
u8 compatibility_type; ///< GameCardCompatibilityType. u8 compatibility_type; ///< GameCardCompatibilityType.
u8 reserved_1[0x3]; u8 reserved_1[0x3];
u64 upp_hash; ///< SHA-256 checksum for the update partition. u64 upp_hash; ///< SHA-256 (?) checksum for the update partition. The exact way it's calculated is currently unknown.
u64 upp_id; ///< Must match GAMECARD_UPDATE_TID. u64 upp_id; ///< Must match GAMECARD_UPDATE_TID.
u8 reserved_2[0x38]; u8 reserved_2[0x38];
} GameCardInfo; } GameCardInfo;
@ -325,8 +331,8 @@ bool gamecardGetTrimmedSize(u64 *out);
/// Fills the provided u64 pointer with the gamecard ROM capacity, based on the GameCardRomSize value from the header. Not the same as gamecardGetTotalSize(). /// Fills the provided u64 pointer with the gamecard ROM capacity, based on the GameCardRomSize value from the header. Not the same as gamecardGetTotalSize().
bool gamecardGetRomCapacity(u64 *out); bool gamecardGetRomCapacity(u64 *out);
/// Fills the provided VersionType1 pointer with the bundled firmware update version in the inserted gamecard. /// Fills the provided Version pointer with the bundled firmware update version in the inserted gamecard.
bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out); bool gamecardGetBundledFirmwareUpdateVersion(Version *out);
/// Fills the provided HashFileSystemContext pointer using information from the requested Hash FS partition. /// Fills the provided HashFileSystemContext pointer using information from the requested Hash FS partition.
/// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the underlying data from the filled context. /// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the underlying data from the filled context.

View file

@ -144,7 +144,7 @@ typedef struct {
u64 content_size; u64 content_size;
u64 program_id; u64 program_id;
u32 content_index; u32 content_index;
VersionType2 sdk_addon_version; SdkAddOnVersion sdk_addon_version;
u8 key_generation; ///< NcaKeyGeneration. Uses NcaKeyGeneration_Since301NUP or greater values. u8 key_generation; ///< NcaKeyGeneration. Uses NcaKeyGeneration_Since301NUP or greater values.
u8 main_signature_key_generation; ///< NcaMainSignatureKeyGeneration. u8 main_signature_key_generation; ///< NcaMainSignatureKeyGeneration.
u8 reserved[0xE]; u8 reserved[0xE];

View file

@ -76,7 +76,7 @@ typedef struct {
u8 main_thread_core_number; ///< Must not exceed NPDM_MAIN_THREAD_MAX_CORE_NUMBER. u8 main_thread_core_number; ///< Must not exceed NPDM_MAIN_THREAD_MAX_CORE_NUMBER.
u8 reserved_3[0x4]; u8 reserved_3[0x4];
u32 system_resource_size; ///< Must not exceed NPDM_SYSTEM_RESOURCE_MAX_SIZE. u32 system_resource_size; ///< Must not exceed NPDM_SYSTEM_RESOURCE_MAX_SIZE.
VersionType1 version; Version version;
u32 main_thread_stack_size; ///< Must be aligned to NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT. u32 main_thread_stack_size; ///< Must be aligned to NPDM_MAIN_THREAD_STACK_SIZE_ALIGNMENT.
char name[0x10]; ///< Usually set to "Application". char name[0x10]; ///< Usually set to "Application".
char product_code[0x10]; ///< Usually zeroed out. char product_code[0x10]; ///< Usually zeroed out.

View file

@ -65,37 +65,43 @@
/* USB Mass Storage support. */ /* USB Mass Storage support. */
#include "ums.h" #include "ums.h"
/// Used to store version numbers expressed in dot notation: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}". /// Used to store version numbers expressed in dot notation:
/// * System version: "{major}.{minor}.{micro}-{major_relstep}.{minor_relstep}".
/// * Application version: "{release}.{private}".
/// Referenced by multiple header files. /// Referenced by multiple header files.
typedef struct { typedef struct {
union { union {
u32 value;
struct { struct {
u32 minor_relstep : 8; u32 minor_relstep : 8;
u32 major_relstep : 8; u32 major_relstep : 8;
u32 micro : 4; u32 micro : 4;
u32 minor : 6; u32 minor : 6;
u32 major : 6; u32 major : 6;
}; } system_version;
u32 value; struct {
u32 private_ver : 16;
u32 release_ver : 16;
} application_version;
}; };
} VersionType1; } Version;
NXDT_ASSERT(VersionType1, 0x4); NXDT_ASSERT(Version, 0x4);
/// Used to store version numbers expressed in dot notation: "{major}.{minor}.{micro}-{relstep}". /// Used to store version numbers expressed in dot notation: "{major}.{minor}.{micro}-{relstep}".
/// Only used by GameCardFwMode and NcaSdkAddOnVersion. /// Only used by GameCardFwMode and NcaSdkAddOnVersion.
typedef struct { typedef struct {
union { union {
u32 value;
struct { struct {
u32 relstep : 8; u32 relstep : 8;
u32 micro : 8; u32 micro : 8;
u32 minor : 8; u32 minor : 8;
u32 major : 8; u32 major : 8;
}; };
u32 value;
}; };
} VersionType2; } SdkAddOnVersion;
NXDT_ASSERT(VersionType2, 0x4); NXDT_ASSERT(SdkAddOnVersion, 0x4);
#endif /* __NXDT_INCLUDES_H__ */ #endif /* __NXDT_INCLUDES_H__ */

View file

@ -148,6 +148,8 @@ void utilsCreateDirectoryTree(const char *path, bool create_last_element);
/// Returns a pointer to a dynamically allocated string that holds the full path formed by the provided arguments. Both path prefix and file extension are optional. /// Returns a pointer to a dynamically allocated string that holds the full path formed by the provided arguments. Both path prefix and file extension are optional.
/// If any elements from the generated path exceed safe filesystem limits, each exceeding element will be truncated. Truncations, if needed, are performed on a per-codepoint basis (UTF-8). /// If any elements from the generated path exceed safe filesystem limits, each exceeding element will be truncated. Truncations, if needed, are performed on a per-codepoint basis (UTF-8).
/// If an extension is provided, it will always be preserved, regardless of any possible truncations being carried out. /// If an extension is provided, it will always be preserved, regardless of any possible truncations being carried out.
/// A path separator is automatically placed between the provided prefix and the filename if the prefix doesn't end with one.
/// A dot *isn't* automatically placed between the filename and the provided extension -- if required, it must be provided as part of the extension string.
/// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned. /// Furthermore, if the full length for the generated path is >= FS_MAX_PATH, NULL will be returned.
char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension); char *utilsGeneratePath(const char *prefix, const char *filename, const char *extension);

View file

@ -49,7 +49,7 @@ typedef struct {
typedef struct _TitleInfo { typedef struct _TitleInfo {
u8 storage_id; ///< NcmStorageId. u8 storage_id; ///< NcmStorageId.
NcmContentMetaKey meta_key; ///< Used with ncm calls. NcmContentMetaKey meta_key; ///< Used with ncm calls.
VersionType1 version; ///< Holds the same value from meta_key.version. Version version; ///< Holds the same value from meta_key.version.
u32 content_count; ///< Content info count. u32 content_count; ///< Content info count.
NcmContentInfo *content_infos; ///< Content info entries from this title. NcmContentInfo *content_infos; ///< Content info entries from this title.
u64 size; ///< Total title size. u64 size; ///< Total title size.

View file

@ -435,7 +435,7 @@ bool gamecardGetRomCapacity(u64 *out)
return ret; return ret;
} }
bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out) bool gamecardGetBundledFirmwareUpdateVersion(Version *out)
{ {
bool ret = false; bool ret = false;

View file

@ -759,11 +759,9 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex
bool use_extension = (extension && *extension); bool use_extension = (extension && *extension);
size_t extension_len = (use_extension ? strlen(extension) : 0); size_t extension_len = (use_extension ? strlen(extension) : 0);
bool append_dot = (use_extension && *extension != '.');
size_t path_len = (prefix_len + strlen(filename) + extension_len); size_t path_len = (prefix_len + strlen(filename) + extension_len);
if (append_path_sep) path_len++; if (append_path_sep) path_len++;
if (append_dot) path_len++;
char *path = NULL, *ptr1 = NULL, *ptr2 = NULL; char *path = NULL, *ptr1 = NULL, *ptr2 = NULL;
bool filename_only = false, success = false; bool filename_only = false, success = false;
@ -779,7 +777,6 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex
if (use_prefix) strcat(path, prefix); if (use_prefix) strcat(path, prefix);
if (append_path_sep) strcat(path, "/"); if (append_path_sep) strcat(path, "/");
strcat(path, filename); strcat(path, filename);
if (append_dot) strcat(path, ".");
if (use_extension) strcat(path, extension); if (use_extension) strcat(path, extension);
/* Retrieve pointer to the first path separator. */ /* Retrieve pointer to the first path separator. */
@ -793,11 +790,14 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex
/* Make sure each path element doesn't exceed NT_MAX_FILENAME_LENGTH. */ /* Make sure each path element doesn't exceed NT_MAX_FILENAME_LENGTH. */
while(ptr1) while(ptr1)
{ {
/* End loop if we find a NULL terminator. */ if (!filename_only)
if (!filename_only && !*ptr1++) break; {
/* End loop if we find a NULL terminator. */
if (!*ptr1++) break;
/* Get pointer to next path separator. */ /* Get pointer to next path separator. */
ptr2 = strchr(ptr1, '/'); ptr2 = strchr(ptr1, '/');
}
/* Get current path element size. */ /* Get current path element size. */
size_t element_size = (ptr2 ? (size_t)(ptr2 - ptr1) : (path_len - (size_t)(ptr1 - path))); size_t element_size = (ptr2 ? (size_t)(ptr2 - ptr1) : (path_len - (size_t)(ptr1 - path)));
@ -819,16 +819,13 @@ char *utilsGeneratePath(const char *prefix, const char *filename, const char *ex
if (use_extension) if (use_extension)
{ {
/* Truncate last element. Make sure to preserve the provided file extension. */ /* Truncate last element. Make sure to preserve the provided file extension. */
size_t diff = extension_len; if (extension_len >= last_cp_pos)
if (append_dot) diff++;
if (diff >= last_cp_pos)
{ {
LOG_MSG("File extension length is >= truncated filename length! (0x%lX >= 0x%lX) (#1).", diff, last_cp_pos); LOG_MSG("File extension length is >= truncated filename length! (0x%lX >= 0x%lX).", extension_len, last_cp_pos);
goto end; goto end;
} }
memmove(ptr1 + last_cp_pos - diff, ptr1 + element_size - diff, diff); memmove(ptr1 + last_cp_pos - extension_len, ptr1 + element_size - extension_len, extension_len);
} }
path_len -= (element_size - last_cp_pos); path_len -= (element_size - last_cp_pos);

View file

@ -104,14 +104,14 @@ namespace nxdt::views
this->total_size->setValue(this->GetFormattedSizeString(&gamecardGetTotalSize)); this->total_size->setValue(this->GetFormattedSizeString(&gamecardGetTotalSize));
this->trimmed_size->setValue(this->GetFormattedSizeString(&gamecardGetTrimmedSize)); this->trimmed_size->setValue(this->GetFormattedSizeString(&gamecardGetTrimmedSize));
const VersionType1 *upp_version = &(card_info.upp_version); const Version *upp_version = &(card_info.upp_version);
this->update_version->setValue(fmt::format("{}.{}.{}-{}.{} (v{})", upp_version->major, upp_version->minor, upp_version->micro, upp_version->major_relstep, \ this->update_version->setValue(fmt::format("{}.{}.{}-{}.{} (v{})", upp_version->system_version.major, upp_version->system_version.minor, upp_version->system_version.micro, \
upp_version->minor_relstep, upp_version->value)); upp_version->system_version.major_relstep, upp_version->system_version.minor_relstep, upp_version->value));
u64 fw_version = card_info.fw_version; u64 fw_version = card_info.fw_version;
this->lafw_version->setValue(fmt::format("{} ({})", fw_version, fw_version >= GameCardFwVersion_Count ? "generic/unknown"_i18n : gamecardGetRequiredHosVersionString(fw_version))); this->lafw_version->setValue(fmt::format("{} ({})", fw_version, fw_version >= GameCardFwVersion_Count ? "generic/unknown"_i18n : gamecardGetRequiredHosVersionString(fw_version)));
const VersionType2 *fw_mode = &(card_info.fw_mode); const SdkAddOnVersion *fw_mode = &(card_info.fw_mode);
this->sdk_version->setValue(fmt::format("{}.{}.{}-{} (v{})", fw_mode->major, fw_mode->minor, fw_mode->micro, fw_mode->relstep, fw_mode->value)); this->sdk_version->setValue(fmt::format("{}.{}.{}-{} (v{})", fw_mode->major, fw_mode->minor, fw_mode->micro, fw_mode->relstep, fw_mode->value));
u8 compatibility_type = card_info.compatibility_type; u8 compatibility_type = card_info.compatibility_type;