diff --git a/source/gamecard.c b/source/gamecard.c index de531e8..70de8fa 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -50,9 +50,10 @@ typedef enum { } GameCardStorageArea; typedef struct { - u64 offset; ///< Relative to the start of the gamecard header. - u64 size; ///< Whole partition size. - u8 *header; ///< GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table. + u64 offset; ///< Relative to the start of the gamecard header. + u64 size; ///< Whole partition size. + u64 header_size; ///< Full header size. + u8 *header; ///< GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table. } GameCardHashFileSystemPartitionInfo; /* Global variables. */ @@ -100,8 +101,12 @@ static void gamecardCloseStorageArea(void); static bool gamecardGetStorageAreasSizes(void); static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size); +static inline GameCardHashFileSystemHeader *gamecardGetHashFileSystemPartitionHeaderByIndex(u32 idx); +static inline GameCardHashFileSystemEntry *gamecardGetHashFileSystemEntryByIndex(void *hfs_header, u32 idx); +static inline char *gamecardGetHashFileSystemEntryName(void *hfs_header, u32 name_offset); + /* Service guard used to generate thread-safe initialize + exit functions. */ -/* I'm using this here even though this actually isn't a service but who cares, it gets the job done. */ +/* I'm using this here even though this actually isn't a real service but who cares, it gets the job done. */ NX_GENERATE_SERVICE_GUARD(gamecard); bool gamecardIsReady(void) @@ -217,15 +222,52 @@ bool gamecardGetBundledFirmwareUpdateVersion(u32 *out) return ret; } - - - - - - - - - +bool gamecardGetHashFileSystemEntryDataOffsetByName(u32 hfs_partition_idx, const char *name, u64 *out_offset) +{ + bool ret = false; + char *entry_name = NULL; + size_t name_len = 0; + GameCardHashFileSystemHeader *fs_header = NULL; + GameCardHashFileSystemEntry *fs_entry = NULL; + + mtx_lock(&g_gameCardSharedDataMutex); + + if (!g_gameCardInserted || !g_gameCardInfoLoaded || !name || !*name || !out_offset) + { + LOGFILE("Invalid parameters!"); + goto out; + } + + name_len = strlen(name); + + fs_header = gamecardGetHashFileSystemPartitionHeaderByIndex(hfs_partition_idx); + if (!fs_header) + { + LOGFILE("Invalid hash FS partition index! (0x%X)", hfs_partition_idx); + goto out; + } + + for(u32 i = 0; i < fs_header->entry_count; i++) + { + fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, i); + if (!fs_entry) continue; + + entry_name = gamecardGetHashFileSystemEntryName(fs_header, fs_entry->name_offset); + if (!entry_name) continue; + + if (!strncasecmp(entry_name, name, name_len)) + { + *out_offset = (g_gameCardHfsPartitions[hfs_partition_idx].offset + g_gameCardHfsPartitions[hfs_partition_idx].header_size + fs_entry->offset); + ret = true; + break; + } + } + +out: + mtx_unlock(&g_gameCardSharedDataMutex); + + return ret; +} @@ -521,11 +563,10 @@ static void gamecardLoadInfo(void) /* Read hash FS partitions */ for(u32 i = 0; i < fs_header->entry_count; i++) { - fs_entry = (GameCardHashFileSystemEntry*)(g_gameCardHfsRootHeader + sizeof(GameCardHashFileSystemHeader) + (i * sizeof(GameCardHashFileSystemEntry))); - - if (!fs_entry->size) + fs_entry = gamecardGetHashFileSystemEntryByIndex(g_gameCardHfsRootHeader, i); + if (!fs_entry || !fs_entry->size) { - LOGFILE("Invalid size for hash FS partition #%u!", i); + LOGFILE("Invalid hash FS partition entry!"); goto out; } @@ -552,11 +593,12 @@ static void gamecardLoadInfo(void) goto out; } - /* Calculate the full header size for the current hash FS partition */ - u64 partition_header_size = (sizeof(GameCardHashFileSystemHeader) + (partition_header.entry_count * sizeof(GameCardHashFileSystemEntry)) + partition_header.name_table_size); + /* Calculate the full header size for the current hash FS partition and round it to a GAMECARD_MEDIA_UNIT_SIZE bytes boundary */ + g_gameCardHfsPartitions[i].header_size = (sizeof(GameCardHashFileSystemHeader) + (partition_header.entry_count * sizeof(GameCardHashFileSystemEntry)) + partition_header.name_table_size); + g_gameCardHfsPartitions[i].header_size = ROUND_UP(g_gameCardHfsPartitions[i].header_size, GAMECARD_MEDIA_UNIT_SIZE); /* Allocate memory for the hash FS partition header */ - g_gameCardHfsPartitions[i].header = calloc(partition_header_size, sizeof(u8)); + g_gameCardHfsPartitions[i].header = calloc(g_gameCardHfsPartitions[i].header_size, sizeof(u8)); if (!g_gameCardHfsPartitions[i].header) { LOGFILE("Unable to allocate memory for the hash FS partition #%u header!", i); @@ -564,7 +606,7 @@ static void gamecardLoadInfo(void) } /* Finally, read the full hash FS partition header */ - if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, partition_header_size, g_gameCardHfsPartitions[i].offset, false)) + if (!gamecardReadStorageArea(g_gameCardHfsPartitions[i].header, g_gameCardHfsPartitions[i].header_size, g_gameCardHfsPartitions[i].offset, false)) { LOGFILE("Failed to read full hash FS partition #%u header from offset 0x%lX!", i, g_gameCardHfsPartitions[i].offset); goto out; @@ -862,3 +904,25 @@ static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size) return capacity; } + +static inline GameCardHashFileSystemHeader *gamecardGetHashFileSystemPartitionHeaderByIndex(u32 idx) +{ + if (idx >= ((GameCardHashFileSystemHeader*)g_gameCardHfsRootHeader)->entry_count) return NULL; + return (GameCardHashFileSystemHeader*)g_gameCardHfsPartitions[idx].header; +} + +static inline GameCardHashFileSystemEntry *gamecardGetHashFileSystemEntryByIndex(void *hfs_header, u32 idx) +{ + if (!hfs_header || idx >= ((GameCardHashFileSystemHeader*)hfs_header)->entry_count) return NULL; + return (GameCardHashFileSystemEntry*)((u8*)hfs_header + sizeof(GameCardHashFileSystemHeader) + (idx * sizeof(GameCardHashFileSystemEntry))); +} + +static inline char *gamecardGetHashFileSystemEntryName(void *hfs_header, u32 name_offset) +{ + if (!hfs_header) return NULL; + + GameCardHashFileSystemHeader *header = (GameCardHashFileSystemHeader*)hfs_header; + if (!header->entry_count || name_offset >= header->name_table_size) return NULL; + + return ((char*)hfs_header + sizeof(GameCardHashFileSystemHeader) + (header->entry_count * sizeof(GameCardHashFileSystemEntry)) + name_offset); +} diff --git a/source/gamecard.h b/source/gamecard.h index 1b7c4ae..9f5d933 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -155,4 +155,6 @@ bool gamecardGetRomCapacity(u64 *out); ///< Not the same as gamecardGetTotalSize bool gamecardGetCertificate(FsGameCardCertificate *out); bool gamecardGetBundledFirmwareUpdateVersion(u32 *out); +bool gamecardGetHashFileSystemEntryDataOffsetByName(u32 hfs_partition_idx, const char *name, u64 *out_offset); + #endif /* __GAMECARD_H__ */ diff --git a/source/keys.c b/source/keys.c index 2c986a0..bf0c37a 100644 --- a/source/keys.c +++ b/source/keys.c @@ -29,6 +29,8 @@ #define SEGMENT_RODATA BIT(1) #define SEGMENT_DATA BIT(2) +/* Type definitions. */ + typedef struct { u64 program_id; u8 mask; @@ -69,6 +71,8 @@ typedef struct { u8 key_area_keys[0x20][3][0x10]; ///< Key area encryption keys. } keysNcaKeyset; +/* Global variables. */ + static keysNcaKeyset g_ncaKeyset = {0}; static keysMemoryInfo g_fsRodataMemoryInfo = { @@ -130,6 +134,8 @@ static keysMemoryInfo g_fsDataMemoryInfo = { } }; +/* Function prototypes. */ + static bool keysRetrieveDebugHandleFromProcessByProgramId(Handle *out, u64 program_id); static bool keysRetrieveProcessMemory(keysMemoryLocation *location); static void keysFreeProcessMemory(keysMemoryLocation *location); diff --git a/source/main.c b/source/main.c index e9ba351..5ee8684 100644 --- a/source/main.c +++ b/source/main.c @@ -65,6 +65,7 @@ int main(int argc, char *argv[]) FsGameCardCertificate cert = {0}; u64 total_size = 0, trimmed_size = 0; u32 update_version = 0; + u64 nca_offset = 0; if (gamecardGetHeader(&header)) { @@ -167,6 +168,15 @@ int main(int argc, char *argv[]) consoleUpdate(NULL); + if (gamecardGetHashFileSystemEntryDataOffsetByName(2, "7e86768383cfabb30f1b58d2373fed07.nca", &nca_offset)) // Should match 0x1657F5E00 + { + printf("nca_offset: 0x%lX\n", nca_offset); + } else { + printf("nca_offset failed\n"); + } + + consoleUpdate(NULL); + SLEEP(3); consoleExit(NULL); diff --git a/source/nca.h b/source/nca.h index 1c4166b..b4bae7c 100644 --- a/source/nca.h +++ b/source/nca.h @@ -265,7 +265,7 @@ typedef struct { typedef struct { u8 storage_id; ///< NcmStorageId. NcmContentStorage *ncm_storage; ///< Pointer to a NcmContentStorage instance. Used to read NCA data. - u64 gc_secure_area_base_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. + u64 gamecard_offset; ///< Used to read NCA data from a gamecard using a FsStorage instance when storage_id == NcmStorageId_GameCard. NcmContentId id; ///< Also used to read NCA data. char id_str[0x21]; u8 hash[0x20]; diff --git a/source/services.c b/source/services.c index e968f5c..dea3d87 100644 --- a/source/services.c +++ b/source/services.c @@ -51,13 +51,13 @@ static ServicesInfoEntry g_serviceInfo[] = { { false, "ns", NULL, &nsInitialize, &nsExit }, { false, "csrng", NULL, &csrngInitialize, &csrngExit }, { false, "spl", NULL, &splInitialize, &splExit }, - { false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice) */ + { false, "spl:mig", &servicesSplCryptoCheckAvailability, &splCryptoInitialize, &splCryptoExit }, /* Checks if spl:mig is really available (e.g. avoid calling splInitialize twice) */ { false, "pm:dmnt", NULL, &pmdmntInitialize, &pmdmntExit }, { false, "pl", NULL, &plInitialize, &plExit }, { false, "psm", NULL, &psmInitialize, &psmExit }, { false, "nifm:u", NULL, &servicesNifmUserInitialize, &nifmExit }, - { false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst */ - { false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available */ + { false, "clk", &servicesClkGetServiceType, NULL, NULL }, /* Placeholder for pcv / clkrst */ + { false, "fsp-usb", &servicesFspUsbCheckAvailability, &fspusbInitialize, &fspusbExit }, /* Checks if fsp-usb is really available */ { false, "es", NULL, &esInitialize, &esExit }, { false, "set:cal", NULL, &setcalInitialize, &setcalExit } }; @@ -92,7 +92,7 @@ bool servicesInitialize(void) rc = g_serviceInfo[i].init_func(); if (R_FAILED(rc)) { - utilsConsoleErrorScreen("%s: failed to initialize %s service! (0x%08X)", __func__, g_serviceInfo[i].name, rc); + LOGFILE("Failed to initialize %s service! (0x%08X)", g_serviceInfo[i].name, rc); ret = false; break; } diff --git a/source/utils.c b/source/utils.c index f1e5d06..d4e70cc 100644 --- a/source/utils.c +++ b/source/utils.c @@ -22,18 +22,23 @@ //#include "freetype_helper.h" //#include "lvgl_helper.h" -#include "services.h" +#include "keys.h" #include "gamecard.h" +#include "services.h" #include "utils.h" /* Global variables. */ +static u8 g_customFirmwareType = UtilsCustomFirmwareType_Unknown; + static AppletHookCookie g_systemOverclockCookie = {0}; static Mutex g_logfileMutex = 0; /* Function prototypes. */ +static void _utilsGetCustomFirmwareType(void); + static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param); u64 utilsHidKeysAllDown(void) @@ -71,22 +76,6 @@ void utilsWaitForButtonPress(void) } } -void utilsConsoleErrorScreen(const char *fmt, ...) -{ - consoleInit(NULL); - - va_list va; - va_start(va, fmt); - vprintf(fmt, va); - va_end(va); - - printf("\nPress any button to exit.\n"); - - consoleUpdate(NULL); - utilsWaitForButtonPress(); - consoleExit(NULL); -} - void utilsWriteLogMessage(const char *func_name, const char *fmt, ...) { mutexLock(&g_logfileMutex); @@ -122,8 +111,19 @@ void utilsOverclockSystem(bool restore) bool utilsInitializeResources(void) { - /* Initialize all needed services */ - if (!servicesInitialize()) return false; + /* Initialize needed services */ + if (!servicesInitialize()) + { + LOGFILE("Failed to initialize needed services!"); + return false; + } + + /* Load NCA keyset */ + if (!keysLoadNcaKeyset()) + { + LOGFILE("Failed to load NCA keyset!"); + return false; + } /* Initialize FreeType */ //if (!freeTypeHelperInitialize()) return false; @@ -131,17 +131,20 @@ bool utilsInitializeResources(void) /* Initialize LVGL */ //if (!lvglHelperInitialize()) return false; + /* Retrieve custom firmware type */ + _utilsGetCustomFirmwareType(); + /* Overclock system */ utilsOverclockSystem(false); /* Setup an applet hook to change the hardware clocks after a system mode change (docked <-> undocked) */ appletHook(&g_systemOverclockCookie, utilsOverclockSystemAppletHook, NULL); - /* Initialize gamecard */ + /* Initialize gamecard interface */ Result rc = gamecardInitialize(); if (R_FAILED(rc)) { - utilsConsoleErrorScreen("gamecard fail\n"); + LOGFILE("Failed to initialize gamecard interface!"); return false; } @@ -150,7 +153,7 @@ bool utilsInitializeResources(void) void utilsCloseResources(void) { - /* Deinitialize gamecard */ + /* Deinitialize gamecard interface */ gamecardExit(); /* Unset our overclock applet hook */ @@ -169,6 +172,31 @@ void utilsCloseResources(void) servicesClose(); } +u8 utilsGetCustomFirmwareType(void) +{ + return g_customFirmwareType; +} + +static void _utilsGetCustomFirmwareType(void) +{ + bool tx_srv = servicesCheckRunningServiceByName("tx"); + bool rnx_srv = servicesCheckRunningServiceByName("rnx"); + + if (!tx_srv && !rnx_srv) + { + /* Atmosphere */ + g_customFirmwareType = UtilsCustomFirmwareType_Atmosphere; + } else + if (tx_srv && !rnx_srv) + { + /* SX OS */ + g_customFirmwareType = UtilsCustomFirmwareType_SXOS; + } else { + /* ReiNX */ + g_customFirmwareType = UtilsCustomFirmwareType_ReiNX; + } +} + static void utilsOverclockSystemAppletHook(AppletHookType hook, void *param) { (void)param; diff --git a/source/utils.h b/source/utils.h index 338709e..144b97c 100644 --- a/source/utils.h +++ b/source/utils.h @@ -36,9 +36,10 @@ typedef enum { - UtilsCustomFirmwareType_Atmosphere = 0, - UtilsCustomFirmwareType_SXOS = 1, - UtilsCustomFirmwareType_ReiNX = 2 + UtilsCustomFirmwareType_Unknown = 0, + UtilsCustomFirmwareType_Atmosphere = 1, + UtilsCustomFirmwareType_SXOS = 2, + UtilsCustomFirmwareType_ReiNX = 3 } UtilsCustomFirmwareType; typedef struct { @@ -56,8 +57,6 @@ u64 utilsHidKeysAllHeld(void); void utilsWaitForButtonPress(void); -void utilsConsoleErrorScreen(const char *fmt, ...); - void utilsWriteLogMessage(const char *func_name, const char *fmt, ...); void utilsOverclockSystem(bool restore); @@ -65,6 +64,8 @@ void utilsOverclockSystem(bool restore); bool utilsInitializeResources(void); void utilsCloseResources(void); +u8 utilsGetCustomFirmwareType(void); ///< UtilsCustomFirmwareType. + static inline FsStorage *utilsGetEmmcBisSystemStorage(void) @@ -72,11 +73,6 @@ static inline FsStorage *utilsGetEmmcBisSystemStorage(void) return NULL; } -static inline u8 utilsGetCustomFirmwareType(void) -{ - return UtilsCustomFirmwareType_Atmosphere; -} - #endif /* __UTILS_H__ */