diff --git a/README.md b/README.md index 0e52816..1ea5b12 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ todo: hfs0 methods - pfs0: full header aligned to 0x20 + pfs0: full header aligned to 0x20 (nsp) diff --git a/source/gamecard.c b/source/gamecard.c index 226dfa1..900f009 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -23,6 +23,8 @@ #include "service_guard.h" #include "utils.h" +#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0" */ + #define GAMECARD_READ_BUFFER_SIZE 0x800000 /* 8 MiB */ #define GAMECARD_ACCESS_WAIT_TIME 3 /* Seconds */ @@ -43,6 +45,22 @@ /* Type definitions. */ +typedef struct { + u32 magic; ///< "HFS0". + u32 entry_count; + u32 name_table_size; + u8 reserved[0x4]; +} GameCardHashFileSystemHeader; + +typedef struct { + u64 offset; + u64 size; + u32 name_offset; + u32 hash_target_size; + u64 hash_target_offset; + u8 hash[SHA256_HASH_SIZE]; +} GameCardHashFileSystemEntry; + typedef enum { GameCardStorageArea_None = 0, GameCardStorageArea_Normal = 1, @@ -53,7 +71,7 @@ typedef struct { 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. + u8 *header; ///< GameCardHashFileSystemHeader + (GameCardHashFileSystemEntry * entry_count) + Name Table. } GameCardHashFileSystemPartitionInfo; /* Global variables. */ @@ -77,7 +95,7 @@ static GameCardHeader g_gameCardHeader = {0}; static u64 g_gameCardStorageNormalAreaSize = 0, g_gameCardStorageSecureAreaSize = 0; static u64 g_gameCardCapacity = 0; -static u8 *g_gameCardHfsRootHeader = NULL; /// GameCardHashFileSystemHeader + GameCardHashFileSystemEntry + Name Table. +static u8 *g_gameCardHfsRootHeader = NULL; /// GameCardHashFileSystemHeader + (entry_count * GameCardHashFileSystemEntry) + Name Table. static GameCardHashFileSystemPartitionInfo *g_gameCardHfsPartitions = NULL; /* Function prototypes. */ @@ -101,9 +119,12 @@ static void gamecardCloseStorageArea(void); static bool gamecardGetStorageAreasSizes(void); static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size); -static bool gamecardGetHashFileSystemPartitionIndexByType(u8 type, u32 *out); -static inline GameCardHashFileSystemEntry *gamecardGetHashFileSystemEntryByIndex(void *hfs_header, u32 idx); -static inline char *gamecardGetHashFileSystemEntryName(void *hfs_header, u32 name_offset); +static GameCardHashFileSystemHeader *gamecardGetHashFileSystemPartitionHeader(u8 hfs_partition_type, u32 *out_hfs_partition_idx); + +static inline GameCardHashFileSystemEntry *gamecardGetHashFileSystemEntryByIndex(void *header, u32 idx); +static inline char *gamecardGetHashFileSystemNameTable(void *header); +static inline char *gamecardGetHashFileSystemEntryNameByIndex(void *header, u32 idx); +static inline bool gamecardGetHashFileSystemEntryIndexByName(void *header, const char *name, u32 *out_idx); /* Service guard used to generate thread-safe initialize + exit functions. */ /* I'm using this here even though this actually isn't a real service but who cares, it gets the job done. */ @@ -120,7 +141,7 @@ bool gamecardIsReady(void) return ret; } -bool gamecardRead(void *out, u64 read_size, u64 offset) +bool gamecardReadStorage(void *out, u64 read_size, u64 offset) { return gamecardReadStorageArea(out, read_size, offset, true); } @@ -214,6 +235,7 @@ bool gamecardGetBundledFirmwareUpdateVersion(u32 *out) { rc = fsDeviceOperatorUpdatePartitionInfo(&g_deviceOperator, &g_gameCardHandle, &update_version, &update_id); if (R_FAILED(rc)) LOGFILE("fsDeviceOperatorUpdatePartitionInfo failed! (0x%08X)", rc); + ret = (R_SUCCEEDED(rc) && update_id == GAMECARD_UPDATE_TID); if (ret) *out = update_version; } @@ -222,41 +244,84 @@ bool gamecardGetBundledFirmwareUpdateVersion(u32 *out) return ret; } -bool gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(u8 hfs_partition_type, const char *name, u64 *out_offset, u64 *out_size) +bool gamecardGetEntryCountFromHashFileSystemPartition(u8 hfs_partition_type, u32 *out_count) +{ + bool ret = false; + GameCardHashFileSystemHeader *fs_header = NULL; + + mtx_lock(&g_gameCardSharedDataMutex); + if (g_gameCardInserted && g_gameCardInfoLoaded && out_count) + { + fs_header = gamecardGetHashFileSystemPartitionHeader(hfs_partition_type, NULL); + if (fs_header) + { + *out_count = fs_header->entry_count; + ret = true; + } else { + LOGFILE("Failed to retrieve hash FS partition header!"); + } + } + mtx_unlock(&g_gameCardSharedDataMutex); + + return ret; +} + +bool gamecardGetEntryInfoFromHashFileSystemPartitionByIndex(u8 hfs_partition_type, u32 idx, u64 *out_offset, u64 *out_size, char **out_name) { bool ret = false; char *entry_name = NULL; - size_t name_len = 0; u32 hfs_partition_idx = 0; GameCardHashFileSystemHeader *fs_header = NULL; GameCardHashFileSystemEntry *fs_entry = NULL; mtx_lock(&g_gameCardSharedDataMutex); - if (!g_gameCardInserted || !g_gameCardInfoLoaded || !name || !*name || (!out_offset && !out_size) || !gamecardGetHashFileSystemPartitionIndexByType(hfs_partition_type, &hfs_partition_idx)) + if (g_gameCardInserted && g_gameCardInfoLoaded && (out_offset || out_size || out_name)) { - LOGFILE("Invalid parameters!"); - goto out; - } - - name_len = strlen(name); - fs_header = (GameCardHashFileSystemHeader*)g_gameCardHfsPartitions[hfs_partition_idx].header; - - 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)) + fs_header = gamecardGetHashFileSystemPartitionHeader(hfs_partition_type, &hfs_partition_idx); + if (!fs_header) { - if (out_offset) *out_offset = (g_gameCardHfsPartitions[hfs_partition_idx].offset + g_gameCardHfsPartitions[hfs_partition_idx].header_size + fs_entry->offset); - if (out_size) *out_size = fs_entry->size; - ret = true; - break; + LOGFILE("Failed to retrieve hash FS partition header!"); + goto out; } + + fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, idx); + if (!fs_entry) + { + LOGFILE("Failed to retrieve hash FS partition entry by index!"); + goto out; + } + + if (out_offset) + { + if (hfs_partition_type == GameCardHashFileSystemPartitionType_Root) + { + *out_offset = g_gameCardHfsPartitions[idx].offset; /* No need to recalculate what we already have */ + } else { + *out_offset = (g_gameCardHfsPartitions[hfs_partition_idx].offset + g_gameCardHfsPartitions[hfs_partition_idx].header_size + fs_entry->offset); + } + } + + if (out_size) *out_size = fs_entry->size; + + if (out_name) + { + entry_name = gamecardGetHashFileSystemEntryNameByIndex(fs_header, idx); + if (!entry_name || !strlen(entry_name)) + { + LOGFILE("Invalid hash FS partition entry name!"); + goto out; + } + + *out_name = strdup(entry_name); + if (!*out_name) + { + LOGFILE("Failed to duplicate hash FS partition entry name!"); + goto out; + } + } + + ret = true; } out: @@ -265,18 +330,57 @@ out: return ret; } - - - - - - - - - - - - +bool gamecardGetEntryInfoFromHashFileSystemPartitionByName(u8 hfs_partition_type, const char *name, u64 *out_offset, u64 *out_size) +{ + bool ret = false; + u32 hfs_partition_idx = 0, fs_entry_idx = 0; + GameCardHashFileSystemHeader *fs_header = NULL; + GameCardHashFileSystemEntry *fs_entry = NULL; + + mtx_lock(&g_gameCardSharedDataMutex); + + if (g_gameCardInserted && g_gameCardInfoLoaded && (out_offset || out_size)) + { + fs_header = gamecardGetHashFileSystemPartitionHeader(hfs_partition_type, &hfs_partition_idx); + if (!fs_header) + { + LOGFILE("Failed to retrieve hash FS partition header!"); + goto out; + } + + if (!gamecardGetHashFileSystemEntryIndexByName(fs_header, name, &fs_entry_idx)) + { + LOGFILE("Failed to retrieve hash FS partition entry index by name!"); + goto out; + } + + fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, fs_entry_idx); + if (!fs_entry) + { + LOGFILE("Failed to retrieve hash FS partition entry by index!"); + goto out; + } + + if (out_offset) + { + if (hfs_partition_type == GameCardHashFileSystemPartitionType_Root) + { + *out_offset = g_gameCardHfsPartitions[fs_entry_idx].offset; /* No need to recalculate what we already have */ + } else { + *out_offset = (g_gameCardHfsPartitions[hfs_partition_idx].offset + g_gameCardHfsPartitions[hfs_partition_idx].header_size + fs_entry->offset); + } + } + + if (out_size) *out_size = fs_entry->size; + + ret = true; + } + +out: + mtx_unlock(&g_gameCardSharedDataMutex); + + return ret; +} NX_INLINE Result _gamecardInitialize(void) { @@ -902,44 +1006,66 @@ static inline u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size) return capacity; } -static bool gamecardGetHashFileSystemPartitionIndexByType(u8 type, u32 *out) +static GameCardHashFileSystemHeader *gamecardGetHashFileSystemPartitionHeader(u8 hfs_partition_type, u32 *out_hfs_partition_idx) { - if (type > GameCardHashFileSystemPartitionType_Secure || !out) return false; + if (hfs_partition_type > GameCardHashFileSystemPartitionType_Secure) return NULL; - char *entry_name = NULL; - GameCardHashFileSystemEntry *fs_entry = NULL; + u32 hfs_partition_idx = 0; GameCardHashFileSystemHeader *fs_header = (GameCardHashFileSystemHeader*)g_gameCardHfsRootHeader; + if (hfs_partition_type != GameCardHashFileSystemPartitionType_Root) + { + if (gamecardGetHashFileSystemEntryIndexByName(fs_header, GAMECARD_HFS_PARTITION_NAME(hfs_partition_type), &hfs_partition_idx)) + { + fs_header = (GameCardHashFileSystemHeader*)g_gameCardHfsPartitions[hfs_partition_idx].header; + if (out_hfs_partition_idx) *out_hfs_partition_idx = hfs_partition_idx; + } else { + fs_header = NULL; + } + } + + return fs_header; +} + +static inline GameCardHashFileSystemEntry *gamecardGetHashFileSystemEntryByIndex(void *header, u32 idx) +{ + if (!header || idx >= ((GameCardHashFileSystemHeader*)header)->entry_count) return NULL; + return (GameCardHashFileSystemEntry*)((u8*)header + sizeof(GameCardHashFileSystemHeader) + (idx * sizeof(GameCardHashFileSystemEntry))); +} + +static inline char *gamecardGetHashFileSystemNameTable(void *header) +{ + GameCardHashFileSystemHeader *fs_header = (GameCardHashFileSystemHeader*)header; + if (!fs_header || !fs_header->entry_count) return NULL; + return ((char*)header + sizeof(GameCardHashFileSystemHeader) + (fs_header->entry_count * sizeof(GameCardHashFileSystemEntry))); +} + +static inline char *gamecardGetHashFileSystemEntryNameByIndex(void *header, u32 idx) +{ + GameCardHashFileSystemEntry *fs_entry = gamecardGetHashFileSystemEntryByIndex(header, idx); + char *name_table = gamecardGetHashFileSystemNameTable(header); + if (!fs_entry || !name_table) return NULL; + return (name_table + fs_entry->name_offset); +} + +static inline bool gamecardGetHashFileSystemEntryIndexByName(void *header, const char *name, u32 *out_idx) +{ + size_t name_len = 0; + GameCardHashFileSystemHeader *fs_header = (GameCardHashFileSystemHeader*)header; + char *name_table = gamecardGetHashFileSystemNameTable(header); + if (!fs_header || !fs_header->entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false; + for(u32 i = 0; i < fs_header->entry_count; i++) { - fs_entry = gamecardGetHashFileSystemEntryByIndex(fs_header, i); + GameCardHashFileSystemEntry *fs_entry = gamecardGetHashFileSystemEntryByIndex(header, i); if (!fs_entry) continue; - entry_name = gamecardGetHashFileSystemEntryName(fs_header, fs_entry->name_offset); - if (!entry_name) continue; - - if (!strcasecmp(entry_name, GAMECARD_HFS_PARTITION_NAME(type))) + if (!strncmp(name_table + fs_entry->name_offset, name, name_len)) { - *out = i; + *out_idx = i; return true; } } return false; } - -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 5ca4257..aacf7a5 100644 --- a/source/gamecard.h +++ b/source/gamecard.h @@ -23,12 +23,12 @@ #define GAMECARD_HEAD_MAGIC 0x48454144 /* "HEAD" */ #define GAMECARD_CERT_MAGIC 0x43455254 /* "CERT" */ -#define GAMECARD_HFS0_MAGIC 0x48465330 /* "HFS0" */ #define GAMECARD_MEDIA_UNIT_SIZE 0x200 -#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : \ - ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown")))) +#define GAMECARD_HFS_PARTITION_NAME(x) ((x) == GameCardHashFileSystemPartitionType_Root ? "root" : ((x) == GameCardHashFileSystemPartitionType_Update ? "update" : \ + ((x) == GameCardHashFileSystemPartitionType_Logo ? "logo" : ((x) == GameCardHashFileSystemPartitionType_Normal ? "normal" : \ + ((x) == GameCardHashFileSystemPartitionType_Secure ? "secure" : "unknown"))))) typedef enum { GameCardKekIndex_Version0 = 0, @@ -118,27 +118,12 @@ typedef struct { GameCardExtendedHeader extended_header; ///< Encrypted using AES-128-CBC with 'xci_header_key', which can't dumped through current methods. } GameCardHeader; -typedef struct { - u32 magic; ///< "HFS0". - u32 entry_count; - u32 name_table_size; - u8 reserved[0x4]; -} GameCardHashFileSystemHeader; - -typedef struct { - u64 offset; - u64 size; - u32 name_offset; - u32 hash_target_size; - u64 hash_target_offset; - u8 hash[SHA256_HASH_SIZE]; -} GameCardHashFileSystemEntry; - typedef enum { - GameCardHashFileSystemPartitionType_Update = 0, - GameCardHashFileSystemPartitionType_Logo = 1, ///< Only available in GameCardFwVersion_Since400NUP gamecards. - GameCardHashFileSystemPartitionType_Normal = 2, - GameCardHashFileSystemPartitionType_Secure = 3 + GameCardHashFileSystemPartitionType_Root = 0, + GameCardHashFileSystemPartitionType_Update = 1, + GameCardHashFileSystemPartitionType_Logo = 2, ///< Only available in GameCardFwVersion_Since400NUP gamecards. + GameCardHashFileSystemPartitionType_Normal = 3, + GameCardHashFileSystemPartitionType_Secure = 4 } GameCardHashFileSystemPartitionType; /// Initializes data needed to access raw gamecard storage areas. @@ -154,10 +139,11 @@ bool gamecardIsReady(void); /// Used to read data from the inserted gamecard. /// All required handles, changes between normal <-> secure storage areas and proper offset calculations are managed internally. -/// offset + read_size should never exceed the value returned by gamecardGetTotalSize(). -bool gamecardRead(void *out, u64 read_size, u64 offset); +/// 'offset' + 'read_size' must not exceed the value returned by gamecardGetTotalSize(). +bool gamecardReadStorage(void *out, u64 read_size, u64 offset); /// Miscellaneous functions. + bool gamecardGetHeader(GameCardHeader *out); bool gamecardGetTotalSize(u64 *out); bool gamecardGetTrimmedSize(u64 *out); @@ -165,6 +151,16 @@ bool gamecardGetRomCapacity(u64 *out); ///< Not the same as gamecardGetTotalSize bool gamecardGetCertificate(FsGameCardCertificate *out); bool gamecardGetBundledFirmwareUpdateVersion(u32 *out); -bool gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(u8 hfs_partition_type, const char *name, u64 *out_offset, u64 *out_size); ///< GameCardHashFileSystemPartitionType. +/// Retrieves the entry count from a hash FS partition. +bool gamecardGetEntryCountFromHashFileSystemPartition(u8 hfs_partition_type, u32 *out_count); + +/// Retrieves info from a hash FS partition entry using an entry index. +/// 'out_offset', 'out_size' or 'out_name' may be set to NULL, but at least one of them must be a valid pointer. +/// If 'out_name' != NULL and the function call succeeds, a pointer to a heap allocated buffer is returned. +bool gamecardGetEntryInfoFromHashFileSystemPartitionByIndex(u8 hfs_partition_type, u32 idx, u64 *out_offset, u64 *out_size, char **out_name); + +/// Retrieves info from a hash FS partition entry using an entry name. +/// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer. +bool gamecardGetEntryInfoFromHashFileSystemPartitionByName(u8 hfs_partition_type, const char *name, u64 *out_offset, u64 *out_size); #endif /* __GAMECARD_H__ */ diff --git a/source/main.c b/source/main.c index 5764a7e..6f00495 100644 --- a/source/main.c +++ b/source/main.c @@ -22,12 +22,14 @@ //#include "lvgl_helper.h" #include "utils.h" -#include "gamecard.h" -#include "nca.h" -#include "cert.h" #include +#include "nca.h" +#include "pfs0.h" + + + int main(int argc, char *argv[]) { (void)argc; @@ -51,364 +53,228 @@ int main(int argc, char *argv[]) if (lvglHelperGetExitFlag()) break; }*/ - - - - consoleInit(NULL); - printf("waiting...\n"); + printf("initializing...\n"); consoleUpdate(NULL); - while(appletMainLoop()) - { - if (gamecardIsReady()) break; - } - + u8 *buf = NULL; FILE *tmp_file = NULL; - GameCardHeader header = {0}; - FsGameCardCertificate cert = {0}; - u64 total_size = 0, trimmed_size = 0; - u32 update_version = 0; - u64 nca_offset = 0, nca_size = 0; + Ticket tik = {0}; + NcaContext *nca_ctx = NULL; + NcmContentStorage ncm_storage = {0}; + + Result rc = 0; mkdir("sdmc:/nxdt_test", 0744); - - if (gamecardGetHeader(&header)) - { - printf("header success\n"); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/header.bin", "wb"); - if (tmp_file) - { - fwrite(&header, 1, sizeof(GameCardHeader), tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("header saved\n"); - } else { - printf("header not saved\n"); - } - } else { - printf("header failed\n"); - } - - consoleUpdate(NULL); - - if (gamecardGetTotalSize(&total_size)) - { - printf("total_size: 0x%lX\n", total_size); - } else { - printf("total_size failed\n"); - } - - consoleUpdate(NULL); - - if (gamecardGetTrimmedSize(&trimmed_size)) - { - printf("trimmed_size: 0x%lX\n", trimmed_size); - } else { - printf("trimmed_size failed\n"); - } - - consoleUpdate(NULL); - - if (gamecardGetCertificate(&cert)) - { - printf("gamecard cert success\n"); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/cert.bin", "wb"); - if (tmp_file) - { - fwrite(&cert, 1, sizeof(FsGameCardCertificate), tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("gamecard cert saved\n"); - } else { - printf("gamecard cert not saved\n"); - } - } else { - printf("gamecard cert failed\n"); - } - - consoleUpdate(NULL); - - if (gamecardGetBundledFirmwareUpdateVersion(&update_version)) - { - printf("update_version: %u\n", update_version); - } else { - printf("update_version failed\n"); - } - - consoleUpdate(NULL); - - u8 *buf = malloc((u64)0x400300); // 4 MiB + 512 bytes + 256 bytes - if (buf) - { - printf("buf succeeded\n"); - consoleUpdate(NULL); - - if (gamecardRead(buf, (u64)0x400300, (u64)0x16F18100)) // force unaligned read that spans both storage areas - { - u32 crc = crc32Calculate(buf, (u64)0x400300); - - printf("read succeeded: %08X\n", crc); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/data.bin", "wb"); - if (tmp_file) - { - fwrite(buf, 1, (u64)0x400300, tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("data saved\n"); - } else { - printf("data not saved\n"); - } - } else { - printf("read failed\n"); - } - } else { - printf("buf failed\n"); - } - - consoleUpdate(NULL); - - // Should match 0x1657F5E00 - if (gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(GameCardHashFileSystemPartitionType_Secure, "7e86768383cfabb30f1b58d2373fed07.nca", &nca_offset, &nca_size)) - { - printf("nca_offset: 0x%lX | nca_size: 0x%lX\n", nca_offset, nca_size); - } else { - printf("nca_offset + nca_size failed\n"); - } - - consoleUpdate(NULL); - - Ticket tik = {0}; - TikCommonBlock *tik_common_blk = NULL; - - u8 *cert_chain = NULL; - u64 cert_chain_size = 0; - - FsRightsId rights_id = { + /*FsRightsId rights_id = { .c = { 0x01, 0x00, 0x82, 0x40, 0x0B, 0xCC, 0x60, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08 } // Untitled Goose Game + };*/ + + // Untitled Goose Game + NcmPackagedContentInfo content_info = { + .hash = { + 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8, + 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 + }, + .info = { + .content_id = { + .c = { 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 } + }, + .size = { + 0x00, 0x40, 0xAD, 0x31, 0x00, 0x00 + }, + .content_type = NcmContentType_Program, + .id_offset = 0 + } }; - if (tikRetrieveTicketByRightsId(&tik, &rights_id, false)) + PartitionFileSystemContext pfs0_ctx = {0}; + PartitionFileSystemEntry *pfs0_entry = NULL; + + buf = malloc(0x400000); + if (!buf) { - printf("tik succeeded\n"); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/tik.bin", "wb"); - if (tmp_file) - { - fwrite(&tik, 1, sizeof(Ticket), tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("tik saved\n"); - } else { - printf("tik not saved\n"); - } - - consoleUpdate(NULL); - - /*tikConvertPersonalizedTicketToCommonTicket(&tik); - - printf("common tik generated\n"); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/common_tik.bin", "wb"); - if (tmp_file) - { - fwrite(&tik, 1, sizeof(Ticket), tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("common tik saved\n"); - } else { - printf("common tik not saved\n"); - } - - consoleUpdate(NULL);*/ - - tik_common_blk = tikGetCommonBlockFromTicket(&tik); - - if (tik_common_blk) - { - cert_chain = certGenerateRawCertificateChainBySignatureIssuer(tik_common_blk->issuer, &cert_chain_size); - if (cert_chain) - { - printf("cert chain succeeded | size: 0x%lX\n", cert_chain_size); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/chain.bin", "wb"); - if (tmp_file) - { - fwrite(cert_chain, 1, cert_chain_size, tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("cert chain saved\n"); - } else { - printf("cert chain not saved\n"); - } - } else { - printf("cert chain failed\n"); - } - } - } else { - printf("tik failed\n"); + printf("read buf failed\n"); + goto out2; } + printf("read buf succeeded\n"); consoleUpdate(NULL); - NcaContext *nca_ctx = calloc(1, sizeof(NcaContext)); - if (nca_ctx) + nca_ctx = calloc(1, sizeof(NcaContext)); + if (!nca_ctx) { - printf("nca ctx buf succeeded\n"); - consoleUpdate(NULL); - - NcmContentStorage ncm_storage = {0}; - if (R_SUCCEEDED(ncmOpenContentStorage(&ncm_storage, NcmStorageId_SdCard))) - { - printf("ncm open storage succeeded\n"); - consoleUpdate(NULL); - - // Untitled Goose Game - NcmPackagedContentInfo content_info = { - .hash = { - 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8, - 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 - }, - .info = { - .content_id = { - .c = { 0x8E, 0xF9, 0x20, 0xD4, 0x5E, 0xE1, 0x9E, 0xD1, 0xD2, 0x04, 0xC4, 0xC8, 0x22, 0x50, 0x79, 0xE8 } - }, - .size = { - 0x00, 0x40, 0xAD, 0x31, 0x00, 0x00 - }, - .content_type = NcmContentType_Program, - .id_offset = 0 - } - }; - - if (ncaInitializeContext(nca_ctx, NcmStorageId_SdCard, &ncm_storage, 0, &content_info, &tik)) - { - printf("nca initialize ctx succeeded\n"); - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/nca_ctx.bin", "wb"); - if (tmp_file) - { - fwrite(nca_ctx, 1, sizeof(NcaContext), tmp_file); - fclose(tmp_file); - tmp_file = NULL; - printf("nca ctx saved\n"); - } else { - printf("nca ctx not saved\n"); - } - - consoleUpdate(NULL); - - tmp_file = fopen("sdmc:/nxdt_test/section0.bin", "wb"); - if (tmp_file) - { - printf("nca section0 created: 0x%lX\n", nca_ctx->fs_contexts[0].section_size); - consoleUpdate(NULL); - - u64 curpos = 0; - u64 blksize = (u64)0x400000; - u64 total = nca_ctx->fs_contexts[0].section_size; - - for(u64 curpos = 0; curpos < total; curpos += blksize) - { - if (blksize > (total - curpos)) blksize = (total - curpos); - - if (!ncaReadFsSection(&(nca_ctx->fs_contexts[0]), buf, blksize, curpos)) - { - printf("nca read section failed\n"); - consoleUpdate(NULL); - break; - } - - fwrite(buf, 1, blksize, tmp_file); - - if (curpos == 0) - { - u8 cryptobuf[0x1E0] = {0}; - u64 block_size = 0, block_offset = 0; - FILE *blktest = NULL; - - u8 *block_data = ncaGenerateEncryptedFsSectionBlock(&(nca_ctx->fs_contexts[0]), buf + 0x809C, 0x1CE, 0x809C, &block_size, &block_offset); - if (block_data) - { - printf("nca generate encrypted block success\n"); - consoleUpdate(NULL); - - blktest = fopen("sdmc:/nxdt_test/blktest.bin", "wb"); - if (blktest) - { - fwrite(block_data, 1, block_size, blktest); - fclose(blktest); - blktest = NULL; - } - - free(block_data); - } - - if (ncaReadContentFile(nca_ctx, cryptobuf, 0x1E0, nca_ctx->fs_contexts[0].section_offset + 0x8090)) - { - printf("nca read encrypted block success\n"); - consoleUpdate(NULL); - - blktest = fopen("sdmc:/nxdt_test/crytobuf.bin", "wb"); - if (blktest) - { - fwrite(cryptobuf, 1, 0x1E0, blktest); - fclose(blktest); - blktest = NULL; - } - } - } - } - - if (curpos >= total) - { - printf("nca read section success\n"); - consoleUpdate(NULL); - } - - fclose(tmp_file); - tmp_file = NULL; - } else { - printf("nca section0 not created\n"); - } - } else { - printf("nca initialize ctx failed\n"); - } - - consoleUpdate(NULL); - - ncmContentStorageClose(&ncm_storage); - } else { - printf("ncm open storage failed\n"); - } - - free(nca_ctx); - } else { printf("nca ctx buf failed\n"); + goto out2; + } + + printf("nca ctx buf succeeded\n"); + consoleUpdate(NULL); + + rc = ncmOpenContentStorage(&ncm_storage, NcmStorageId_SdCard); + if (R_FAILED(rc)) + { + printf("ncm open storage failed\n"); + goto out2; + } + + printf("ncm open storage succeeded\n"); + consoleUpdate(NULL); + + if (!ncaInitializeContext(nca_ctx, NcmStorageId_SdCard, &ncm_storage, 0, &content_info, &tik)) + { + printf("nca initialize ctx failed\n"); + goto out2; + } + + tmp_file = fopen("sdmc:/nxdt_test/nca_ctx.bin", "wb"); + if (tmp_file) + { + fwrite(nca_ctx, 1, sizeof(NcaContext), tmp_file); + fclose(tmp_file); + tmp_file = NULL; + printf("nca ctx saved\n"); + } else { + printf("nca ctx not saved\n"); } consoleUpdate(NULL); - - while(true) + tmp_file = fopen("sdmc:/nxdt_test/section0.bin", "wb"); + if (tmp_file) { + u64 blksize = 0x400000; + u64 total = nca_ctx->fs_contexts[0].section_size; + + printf("nca section0 created: 0x%lX\n", total); + consoleUpdate(NULL); + + for(u64 curpos = 0; curpos < total; curpos += blksize) + { + if (blksize > (total - curpos)) blksize = (total - curpos); + + if (!ncaReadFsSection(&(nca_ctx->fs_contexts[0]), buf, blksize, curpos)) + { + printf("nca read section failed\n"); + goto out2; + } + + fwrite(buf, 1, blksize, tmp_file); + } + + fclose(tmp_file); + tmp_file = NULL; + + printf("nca read section0 success\n"); + } else { + printf("nca section0 not created\n"); + } + + consoleUpdate(NULL); + + if (!pfs0InitializeContext(&pfs0_ctx, &(nca_ctx->fs_contexts[0]))) + { + printf("pfs0 initialize ctx failed\n"); + goto out2; + } + + printf("pfs0 initialize ctx succeeded\n"); + consoleUpdate(NULL); + + tmp_file = fopen("sdmc:/nxdt_test/pfs0_ctx.bin", "wb"); + if (tmp_file) + { + fwrite(&pfs0_ctx, 1, sizeof(PartitionFileSystemContext), tmp_file); + fclose(tmp_file); + tmp_file = NULL; + printf("pfs0 ctx saved\n"); + } else { + printf("pfs0 ctx not saved\n"); + } + + consoleUpdate(NULL); + + tmp_file = fopen("sdmc:/nxdt_test/pfs0_header.bin", "wb"); + if (tmp_file) + { + fwrite(pfs0_ctx.header, 1, pfs0_ctx.header_size, tmp_file); + fclose(tmp_file); + tmp_file = NULL; + printf("pfs0 header saved\n"); + } else { + printf("pfs0 header not saved\n"); + } + + consoleUpdate(NULL); + + pfs0_entry = pfs0GetEntryByName(&pfs0_ctx, "main.npdm"); + if (!pfs0_entry) + { + printf("pfs0 get entry by name failed\n"); + goto out2; + } + + printf("pfs0 get entry by name succeeded\n"); + consoleUpdate(NULL); + + u64 main_npdm_offset = 0; + if (!pfs0GetEntryDataOffset(&pfs0_ctx, pfs0_entry, &main_npdm_offset)) + { + printf("pfs0 get entry data offset failed\n"); + goto out2; + } + + printf("main.npdm offset = 0x%lX\n", main_npdm_offset); + consoleUpdate(NULL); + + tmp_file = fopen("sdmc:/nxdt_test/main.npdm", "wb"); + if (tmp_file) + { + u64 blksize = 0x400000; + u64 total = pfs0_entry->size; + + printf("main.npdm created: 0x%lX\n", total); + consoleUpdate(NULL); + + for(u64 curpos = 0; curpos < total; curpos += blksize) + { + if (blksize > (total - curpos)) blksize = (total - curpos); + + if (!ncaReadFsSection(pfs0_ctx.nca_fs_ctx, buf, blksize, main_npdm_offset + curpos)) + { + printf("nca read section failed\n"); + goto out2; + } + + fwrite(buf, 1, blksize, tmp_file); + } + + fclose(tmp_file); + tmp_file = NULL; + + printf("nca read main.npdm success\n"); + } else { + printf("main.npdm not created\n"); + } + +out2: + while(appletMainLoop()) + { + consoleUpdate(NULL); hidScanInput(); if (utilsHidKeysAllDown() & KEY_A) break; } + if (tmp_file) fclose(tmp_file); + + pfs0FreeContext(&pfs0_ctx); + + if (serviceIsActive(&(ncm_storage.s))) ncmContentStorageClose(&ncm_storage); + + if (nca_ctx) free(nca_ctx); if (buf) free(buf); diff --git a/source/nca.c b/source/nca.c index 6e9278b..bf928dd 100644 --- a/source/nca.c +++ b/source/nca.c @@ -222,7 +222,7 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, NcmContentStorage *ncm char nca_filename[0x30] = {0}; sprintf(nca_filename, "%s.%s", out->content_id_str, out->content_type == NcmContentType_Meta ? "cnmt.nca" : "nca"); - if (!gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(hfs_partition_type, nca_filename, &(out->gamecard_offset), NULL)) + if (!gamecardGetEntryInfoFromHashFileSystemPartitionByName(hfs_partition_type, nca_filename, &(out->gamecard_offset), NULL)) { LOGFILE("Error retrieving offset for \"%s\" entry in secure hash FS partition!", nca_filename); return false; @@ -380,7 +380,7 @@ bool ncaReadContentFile(NcaContext *ctx, void *out, u64 read_size, u64 offset) } else { /* Retrieve NCA data using raw gamecard reads */ /* Fixes NCA read issues with gamecards under HOS < 4.0.0 when using ncmContentStorageReadContentIdFile() */ - ret = gamecardRead(out, read_size, ctx->gamecard_offset + offset); + ret = gamecardReadStorage(out, read_size, ctx->gamecard_offset + offset); if (!ret) LOGFILE("Failed to read 0x%lX bytes block at offset 0x%lX from NCA \"%s\"! (gamecard)", read_size, offset, ctx->content_id_str); } diff --git a/source/nca.h b/source/nca.h index 8290522..f7988cb 100644 --- a/source/nca.h +++ b/source/nca.h @@ -292,7 +292,7 @@ typedef struct { bool ncaAllocateCryptoBuffer(void); void ncaFreeCryptoBuffer(void); -/// Initializes a valid NCA context. +/// Initializes a NCA context. /// If 'storage_id' != NcmStorageId_GameCard, the 'ncm_storage' argument must point to a valid NcmContentStorage instance, previously opened using the same NcmStorageId value. /// If 'storage_id' == NcmStorageId_GameCard, the 'hfs_partition_type' argument must be a valid GameCardHashFileSystemPartitionType value. /// If the NCA holds a populated Rights ID field, and if the Ticket object pointed to by 'tik' hasn't been filled, ticket data will be retrieved. @@ -327,11 +327,9 @@ bool ncaEncryptHeader(NcaContext *ctx); static inline void ncaConvertNcmContentSizeToU64(const u8 *size, u64 *out) { - if (size && out) - { - *out = 0; - memcpy(out, size, 6); - } + if (!size || !out) return; + *out = 0; + memcpy(out, size, 6); } static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out) @@ -341,20 +339,16 @@ static inline void ncaConvertU64ToNcmContentSize(const u64 *size, u8 *out) static inline void ncaSetDownloadDistributionType(NcaContext *ctx) { - if (ctx && ctx->header.distribution_type != NcaDistributionType_Download) - { - ctx->header.distribution_type = NcaDistributionType_Download; - ctx->dirty_header = true; - } + if (!ctx || ctx->header.distribution_type == NcaDistributionType_Download) return; + ctx->header.distribution_type = NcaDistributionType_Download; + ctx->dirty_header = true; } static inline void ncaWipeRightsId(NcaContext *ctx) { - if (ctx) - { - memset(&(ctx->header.rights_id), 0, sizeof(FsRightsId)); - ctx->dirty_header = true; - } + if (!ctx || !ctx->rights_id_available) return; + memset(&(ctx->header.rights_id), 0, sizeof(FsRightsId)); + ctx->dirty_header = true; } #endif /* __NCA_H__ */ diff --git a/source/pfs0.c b/source/pfs0.c new file mode 100644 index 0000000..8a1eea1 --- /dev/null +++ b/source/pfs0.c @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2020 DarkMatterCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include +#include +#include + +#include "pfs0.h" +#include "utils.h" + +#define PFS0_NCA_FS_HEADER_LAYER_COUNT 2 + +#define NPDM_META_MAGIC 0x4D455441 /* "META" */ + +bool pfs0InitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx) +{ + if (!out || !nca_fs_ctx || nca_fs_ctx->section_type != NcaFsSectionType_PartitionFs || !nca_fs_ctx->header || nca_fs_ctx->header->fs_type != NcaFsType_PartitionFs || \ + nca_fs_ctx->header->hash_type != NcaHashType_HierarchicalSha256) + { + LOGFILE("Invalid parameters!"); + return false; + } + + /* Fill context */ + out->nca_fs_ctx = nca_fs_ctx; + out->hash_info = &(nca_fs_ctx->header->hash_info.hierarchical_sha256); + out->offset = 0; + out->size = 0; + out->is_exefs = false; + out->header_size = 0; + out->header = NULL; + + if (!out->hash_info->hash_block_size || out->hash_info->layer_count != PFS0_NCA_FS_HEADER_LAYER_COUNT || out->hash_info->hash_data_layer_info.offset >= out->nca_fs_ctx->section_size || \ + !out->hash_info->hash_data_layer_info.size || (out->hash_info->hash_data_layer_info.offset + out->hash_info->hash_data_layer_info.size) > out->nca_fs_ctx->section_size || \ + out->hash_info->hash_target_layer_info.offset >= out->nca_fs_ctx->section_size || !out->hash_info->hash_target_layer_info.size || \ + (out->hash_info->hash_target_layer_info.offset + out->hash_info->hash_target_layer_info.size) > out->nca_fs_ctx->section_size) + { + LOGFILE("Invalid HierarchicalSha256 block!"); + return false; + } + + out->offset = out->hash_info->hash_target_layer_info.offset; + out->size = out->hash_info->hash_target_layer_info.size; + + /* Read partial PFS0 header */ + u32 magic = 0; + PartitionFileSystemHeader pfs0_header = {0}; + + u64 main_npdm_offset = 0; + PartitionFileSystemEntry *main_npdm_entry = NULL; + + if (!ncaReadFsSection(nca_fs_ctx, &pfs0_header, sizeof(PartitionFileSystemHeader), out->offset)) + { + LOGFILE("Failed to read partial PFS0 header!"); + return false; + } + + magic = __builtin_bswap32(pfs0_header.magic); + if (magic != PFS0_MAGIC) + { + LOGFILE("Invalid PFS0 magic word! (0x%08X)", magic); + return false; + } + + if (!pfs0_header.entry_count || !pfs0_header.name_table_size) + { + LOGFILE("Invalid PFS0 entry count / name table size!"); + return false; + } + + /* Calculate full PFS0 header size */ + out->header_size = (sizeof(PartitionFileSystemHeader) + (pfs0_header.entry_count * sizeof(PartitionFileSystemEntry)) + pfs0_header.name_table_size); + + /* Allocate memory for the full PFS0 header */ + out->header = calloc(out->header_size, sizeof(u8)); + if (!out->header) + { + LOGFILE("Unable to allocate 0x%lX bytes buffer for the full PFS0 header!", out->header_size); + return false; + } + + /* Read full PFS0 header */ + if (!ncaReadFsSection(nca_fs_ctx, out->header, out->header_size, out->offset)) + { + LOGFILE("Failed to read full PFS0 header!"); + return false; + } + + /* Check if we're dealing with an ExeFS section */ + if ((main_npdm_entry = pfs0GetEntryByName(out, "main.npdm")) != NULL && pfs0GetEntryDataOffset(out, main_npdm_entry, &main_npdm_offset) && \ + ncaReadFsSection(out->nca_fs_ctx, &magic, sizeof(u32), main_npdm_offset) && __builtin_bswap32(magic) == NPDM_META_MAGIC) out->is_exefs = true; + + return true; +} diff --git a/source/pfs0.h b/source/pfs0.h new file mode 100644 index 0000000..7470ee7 --- /dev/null +++ b/source/pfs0.h @@ -0,0 +1,127 @@ +/* + * Copyright (c) 2020 DarkMatterCore + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#pragma once + +#ifndef __PFS0_H__ +#define __PFS0_H__ + +#include +#include "nca.h" + +#define PFS0_MAGIC 0x50465330 /* "PFS0" */ + +typedef struct { + u32 magic; ///< "PFS0". + u32 entry_count; + u32 name_table_size; + u8 reserved[0x4]; +} PartitionFileSystemHeader; + +typedef struct { + u64 offset; + u64 size; + u32 name_offset; + u8 reserved[0x4]; +} PartitionFileSystemEntry; + +typedef struct { + NcaFsSectionContext *nca_fs_ctx; ///< Used to read NCA FS section data. + NcaHierarchicalSha256 *hash_info; ///< Hash table information. + u64 offset; ///< Partition offset (relative to the start of the NCA FS section). + u64 size; ///< Partition size. + bool is_exefs; ///< ExeFS flag. + u64 header_size; ///< Full header size. + u8 *header; ///< PartitionFileSystemHeader + (PartitionFileSystemEntry * entry_count) + Name Table. +} PartitionFileSystemContext; + +/// Initializes a PFS0 context. +bool pfs0InitializeContext(PartitionFileSystemContext *out, NcaFsSectionContext *nca_fs_ctx); + +/// Cleanups a previously initialized PFS0 context. +static inline void pfs0FreeContext(PartitionFileSystemContext *ctx) +{ + if (!ctx) return; + if (ctx->header) free(ctx->header); + memset(ctx, 0, sizeof(PartitionFileSystemContext)); +} + +/// Miscellaneous functions. + +static inline u32 pfs0GetEntryCount(PartitionFileSystemContext *ctx) +{ + if (!ctx || !ctx->header_size || !ctx->header) return 0; + return ((PartitionFileSystemHeader*)ctx->header)->entry_count; +} + +static inline bool pfs0GetEntryDataOffset(PartitionFileSystemContext *ctx, PartitionFileSystemEntry *fs_entry, u64 *out_offset) +{ + if (!ctx || !ctx->header_size || !ctx->header || !fs_entry || !out_offset) return false; + *out_offset = (ctx->offset + ctx->header_size + fs_entry->offset); /* Relative to the start of the NCA FS section */ + return true; +} + +static inline PartitionFileSystemEntry *pfs0GetEntryByIndex(PartitionFileSystemContext *ctx, u32 idx) +{ + if (idx >= pfs0GetEntryCount(ctx)) return NULL; + return (PartitionFileSystemEntry*)(ctx->header + sizeof(PartitionFileSystemHeader) + (idx * sizeof(PartitionFileSystemEntry))); +} + +static inline char *pfs0GetNameTable(PartitionFileSystemContext *ctx) +{ + u32 entry_count = pfs0GetEntryCount(ctx); + if (!entry_count) return NULL; + return (char*)(ctx->header + sizeof(PartitionFileSystemHeader) + (entry_count * sizeof(PartitionFileSystemEntry))); +} + +static inline char *pfs0GetEntryNameByIndex(PartitionFileSystemContext *ctx, u32 idx) +{ + PartitionFileSystemEntry *fs_entry = pfs0GetEntryByIndex(ctx, idx); + char *name_table = pfs0GetNameTable(ctx); + if (!fs_entry || !name_table) return NULL; + return (name_table + fs_entry->name_offset); +} + +static inline bool pfs0GetEntryIndexByName(PartitionFileSystemContext *ctx, const char *name, u32 *out_idx) +{ + size_t name_len = 0; + u32 entry_count = pfs0GetEntryCount(ctx); + char *name_table = pfs0GetNameTable(ctx); + if (!entry_count || !name_table || !name || !(name_len = strlen(name)) || !out_idx) return false; + + for(u32 i = 0; i < entry_count; i++) + { + PartitionFileSystemEntry *fs_entry = pfs0GetEntryByIndex(ctx, i); + if (!fs_entry) continue; + + if (!strncmp(name_table + fs_entry->name_offset, name, name_len)) + { + *out_idx = i; + return true; + } + } + + return false; +} + +static inline PartitionFileSystemEntry *pfs0GetEntryByName(PartitionFileSystemContext *ctx, const char *name) +{ + u32 idx = 0; + if (!pfs0GetEntryIndexByName(ctx, name, &idx)) return NULL; + return pfs0GetEntryByIndex(ctx, idx); +} + +#endif /* __PFS0_H__ */ diff --git a/source/tik.c b/source/tik.c index 06bfa71..38e9281 100644 --- a/source/tik.c +++ b/source/tik.c @@ -204,7 +204,7 @@ static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsI utilsGenerateHexStringFromData(tik_filename, sizeof(tik_filename), id->c, 0x10); strcat(tik_filename, ".tik"); - if (!gamecardGetOffsetAndSizeFromHashFileSystemPartitionEntryByName(GameCardHashFileSystemPartitionType_Secure, tik_filename, &tik_offset, &tik_size)) + if (!gamecardGetEntryInfoFromHashFileSystemPartitionByName(GameCardHashFileSystemPartitionType_Secure, tik_filename, &tik_offset, &tik_size)) { LOGFILE("Error retrieving offset and size for \"%s\" entry in secure hash FS partition!"); return false; @@ -216,7 +216,7 @@ static bool tikRetrieveTicketFromGameCardByRightsId(Ticket *dst, const FsRightsI return false; } - if (!gamecardRead(dst->data, tik_size, tik_offset)) + if (!gamecardReadStorage(dst->data, tik_size, tik_offset)) { LOGFILE("Failed to read \"%s\" data from the inserted gamecard!", tik_filename); return false;