diff --git a/code_templates/nxdt_rw_poc.c b/code_templates/nxdt_rw_poc.c index a0171e0..bbea7f3 100644 --- a/code_templates/nxdt_rw_poc.c +++ b/code_templates/nxdt_rw_poc.c @@ -188,6 +188,7 @@ static bool saveGameCardCertificate(void *userdata); static bool saveGameCardInitialData(void *userdata); static bool saveGameCardSpecificData(void *userdata); static bool saveGameCardIdSet(void *userdata); +static bool saveGameCardUid(void *userdata); static bool saveGameCardHfsPartition(void *userdata); static bool saveGameCardRawHfsPartition(HashFileSystemContext *hfs_ctx); static bool saveGameCardExtractedHfsPartition(HashFileSystemContext *hfs_ctx); @@ -445,16 +446,9 @@ static MenuElement *g_gameCardMenuElements[] = { .userdata = NULL }, &(MenuElement){ - .str = "dump gamecard header", + .str = "dump gamecard initial data", .child_menu = NULL, - .task_func = &saveGameCardHeader, - .element_options = NULL, - .userdata = NULL - }, - &(MenuElement){ - .str = "dump gamecard cardinfo", - .child_menu = NULL, - .task_func = &saveGameCardCardInfo, + .task_func = &saveGameCardInitialData, .element_options = NULL, .userdata = NULL }, @@ -465,20 +459,6 @@ static MenuElement *g_gameCardMenuElements[] = { .element_options = NULL, .userdata = NULL }, - &(MenuElement){ - .str = "dump gamecard initial data", - .child_menu = NULL, - .task_func = &saveGameCardInitialData, - .element_options = NULL, - .userdata = NULL - }, - &(MenuElement){ - .str = "dump gamecard specific data", - .child_menu = NULL, - .task_func = &saveGameCardSpecificData, - .element_options = NULL, - .userdata = NULL - }, &(MenuElement){ .str = "dump gamecard id set", .child_menu = NULL, @@ -487,7 +467,35 @@ static MenuElement *g_gameCardMenuElements[] = { .userdata = NULL }, &(MenuElement){ - .str = "dump hfs partitions", + .str = "dump gamecard uid", + .child_menu = NULL, + .task_func = &saveGameCardUid, + .element_options = NULL, + .userdata = NULL + }, + &(MenuElement){ + .str = "dump gamecard header (optional)", + .child_menu = NULL, + .task_func = &saveGameCardHeader, + .element_options = NULL, + .userdata = NULL + }, + &(MenuElement){ + .str = "dump gamecard cardinfo (optional)", + .child_menu = NULL, + .task_func = &saveGameCardCardInfo, + .element_options = NULL, + .userdata = NULL + }, + &(MenuElement){ + .str = "dump gamecard specific data (optional)", + .child_menu = NULL, + .task_func = &saveGameCardSpecificData, + .element_options = NULL, + .userdata = NULL + }, + &(MenuElement){ + .str = "dump hfs partitions (optional)", .child_menu = &(Menu){ .id = MenuId_HFS, .parent = NULL, @@ -500,7 +508,7 @@ static MenuElement *g_gameCardMenuElements[] = { .userdata = NULL }, &(MenuElement){ - .str = "dump console lafw blob", + .str = "dump console lafw blob (optional)", .child_menu = NULL, .task_func = &saveConsoleLafwBlob, .element_options = NULL, @@ -1046,6 +1054,10 @@ int main(int argc, char *argv[]) consolePrint("______________________________\n\n"); } } + } else + if (cur_menu->id == MenuId_GameCard) { + consolePrint("For a full gamecard image: dump XCI, initial data, certificate, id set and uid.\n"); + consolePrint("______________________________\n\n"); } for(u32 i = cur_menu->scroll; i < element_count; i++) @@ -2597,6 +2609,11 @@ end: return success; } +/* This will save the Gamecard Specific Data. Its format is specific and internal to the current LAFW firmware version and session of the GCBRG ASIC. */ +/* Depending on which Switch system version the gamecard was dumped from, this data can change. */ +/* Even re-inserting the gamecard will change parts of this data. */ +/* For this reason the gamecard specific data is mostly uninteresting for gamecard preservation. */ +/* Instead, take a look at saveGameCardIdSet and saveGameCardUid which is a more standardised format of the Gamecard ID data. */ static bool saveGameCardSpecificData(void *userdata) { (void)userdata; @@ -2657,6 +2674,39 @@ end: return success; } + +static bool saveGameCardUid(void *userdata) +{ + (void)userdata; + + GameCardSecurityInformation gc_security_information = {0}; + bool success = false; + u32 crc = 0; + char *filename = NULL; + + if (!gamecardGetSecurityInformation(&gc_security_information)) + { + consolePrint("failed to get gamecard security information\n"); + goto end; + } + + crc = crc32Calculate(gc_security_information.specific_data.card_uid, sizeof(gc_security_information.specific_data.card_uid)); + snprintf(path, MAX_ELEMENTS(path), " (Card UID) (%08X).bin", crc); + + filename = generateOutputGameCardFileName("Gamecard", path, true); + if (!filename) goto end; + + if (!saveFileData(filename, gc_security_information.specific_data.card_uid, sizeof(gc_security_information.specific_data.card_uid))) goto end; + + consolePrint("successfully saved gamecard uid as \"%s\"\n", filename); + success = true; + +end: + if (filename) free(filename); + + return success; +} + static bool saveGameCardHfsPartition(void *userdata) { u32 hfs_partition_type = (userdata ? *((u32*)userdata) : HashFileSystemPartitionType_None); diff --git a/include/core/fs_ext.h b/include/core/fs_ext.h index 3524d3c..32618f9 100644 --- a/include/core/fs_ext.h +++ b/include/core/fs_ext.h @@ -108,9 +108,9 @@ NXDT_ASSERT(FsCardId3, 0x4); /// Returned by fsDeviceOperatorGetGameCardIdSet. typedef struct { - FsCardId1 id1; - FsCardId2 id2; - FsCardId3 id3; + FsCardId1 id1; ///< Specifies maker code, memory capacity, and memory type + FsCardId2 id2; ///< Specifies card security number and card type + FsCardId3 id3; ///< Always zero (so far) } FsGameCardIdSet; NXDT_ASSERT(FsGameCardIdSet, 0xC); diff --git a/source/core/gamecard.c b/source/core/gamecard.c index b0236b6..2796b59 100644 --- a/source/core/gamecard.c +++ b/source/core/gamecard.c @@ -961,10 +961,6 @@ static bool gamecardReadSecurityInformation(GameCardSecurityInformation *out) /* Jackpot. */ memcpy(out, g_fsProgramMemory.data + offset + sizeof(GameCardInitialData) - sizeof(GameCardSecurityInformation), sizeof(GameCardSecurityInformation)); - /* Clear out the current ASIC session hash. */ - /* It's not actually part of the gamecard data, and this changes every time a gamecard (re)insertion takes place. */ - memset(out->specific_data.asic_session_hash, 0xFF, sizeof(out->specific_data.asic_session_hash)); - found = true; break; }