diff --git a/Makefile b/Makefile index f35e545..2ccd923 100644 --- a/Makefile +++ b/Makefile @@ -35,7 +35,7 @@ VERSION_MAJOR := 1 VERSION_MINOR := 2 VERSION_MICRO := 0 -APP_TITLE := nxdumptool +APP_TITLE := nxdumptool-rewrite APP_AUTHOR := DarkMatterCore APP_VERSION := ${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_MICRO} diff --git a/source/gamecard.c b/source/gamecard.c index a7b775c..fc91a34 100644 --- a/source/gamecard.c +++ b/source/gamecard.c @@ -215,8 +215,6 @@ void gamecardExit(void) { mutexLock(&g_gamecardMutex); - if (!g_gamecardInterfaceInit) goto end; - /* Destroy gamecard detection thread. */ if (g_gameCardDetectionThreadCreated) { @@ -254,7 +252,6 @@ void gamecardExit(void) g_gamecardInterfaceInit = false; -end: mutexUnlock(&g_gamecardMutex); } diff --git a/source/main.c b/source/main.c index e26315c..b48cb43 100644 --- a/source/main.c +++ b/source/main.c @@ -212,6 +212,27 @@ int main(int argc, char *argv[]) goto out; } + + + + + goto out; + + + + + + + + + + + + + + + + Result rc = 0; u8 *buf = NULL; diff --git a/source/title.c b/source/title.c new file mode 100644 index 0000000..ac2633f --- /dev/null +++ b/source/title.c @@ -0,0 +1,692 @@ +/* + * title.c + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool 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. + * + * nxdumptool 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 "utils.h" +#include "title.h" +#include "gamecard.h" + +#define NS_APPLICATION_RECORD_LIMIT 4096 + +/* Global variables. */ + +static Mutex g_titleMutex = 0; +static bool g_titleInterfaceInit = false, g_titleGameCardAvailable = false; + +static NsApplicationControlData *g_nsAppControlData = NULL; + +static TitleApplicationMetadata *g_appMetadata = NULL; +static u32 g_appMetadataCount = 0; + +static NcmContentMetaDatabase g_ncmDbGameCard = {0}, g_ncmDbEmmcSystem = {0}, g_ncmDbEmmcUser = {0}, g_ncmDbSdCard = {0}; +static NcmContentStorage g_ncmStorageGameCard = {0}, g_ncmStorageEmmcSystem = {0}, g_ncmStorageEmmcUser = {0}, g_ncmStorageSdCard = {0}; + +static TitleInfo *g_titleInfo = NULL; +static u32 g_titleInfoCount = 0; + +/* Function prototypes. */ + +static bool titleRetrieveApplicationMetadataFromNsRecords(void); +static bool titleRetrieveApplicationMetadataByTitleId(u64 title_id, TitleApplicationMetadata *out); + +static bool titleOpenNcmDatabases(void); +static void titleCloseNcmDatabases(void); + +static bool titleOpenNcmStorages(void); +static void titleCloseNcmStorages(void); + +static bool titleLoadTitleInfo(void); +static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id, NcmContentMetaDatabase *ncm_db); + +NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id); + +bool titleInitialize(void) +{ + mutexLock(&g_titleMutex); + + bool ret = g_titleInterfaceInit; + if (ret) goto end; + + + + + + while(!gamecardGetStatusChangeUserEvent()); + + + + + /* Allocate memory for the ns application control data. */ + /* This will be used each time we need to retrieve application metadata. */ + g_nsAppControlData = calloc(1, sizeof(NsApplicationControlData)); + if (!g_nsAppControlData) + { + LOGFILE("Failed to allocate memory for the ns application control data!"); + goto end; + } + + /* Retrieve application metadata from ns records. */ + /* Theoretically speaking, we should only need to do this once. */ + /* However, if any new gamecard is inserted while the application is running, we *will* have to retrieve the metadata from its application(s). */ + if (!titleRetrieveApplicationMetadataFromNsRecords()) + { + LOGFILE("Failed to retrieve application metadata from ns records!"); + goto end; + } + + /* Open eMMC System, eMMC User and SD card ncm databases. */ + if (!titleOpenNcmDatabases()) + { + LOGFILE("Failed to open ncm databases!"); + goto end; + } + + /* Open eMMC System, eMMC User and SD card ncm storages. */ + if (!titleOpenNcmStorages()) + { + LOGFILE("Failed to open ncm storages!"); + goto end; + } + + /* Load title info by retrieving content meta keys from available eMMC System, eMMC User and SD card titles. */ + if (!titleLoadTitleInfo()) + { + LOGFILE("Failed to load title info!"); + goto end; + } + + + + + + + + + + + + + + + if (g_titleInfo && g_titleInfoCount) + { + mkdir("sdmc:/records", 0777); + + FILE *title_infos_txt = NULL, *icon_jpg = NULL; + char icon_path[FS_MAX_PATH] = {0}; + + title_infos_txt = fopen("sdmc:/records/title_infos.txt", "wb"); + if (title_infos_txt) + { + for(u32 i = 0; i < g_titleInfoCount; i++) + { + TitleVersion version; + memcpy(&version, &(g_titleInfo[i].meta_key.version), sizeof(u32)); + + fprintf(title_infos_txt, "Storage ID: 0x%02X\r\n", g_titleInfo[i].storage_id); + fprintf(title_infos_txt, "Title ID: %016lX\r\n", g_titleInfo[i].meta_key.id); + fprintf(title_infos_txt, "Version: %u (%u.%u.%u-%u.%u)\r\n", g_titleInfo[i].meta_key.version, version.TitleVersion_Major, version.TitleVersion_Minor, version.TitleVersion_Micro, \ + version.TitleVersion_MajorRelstep, version.TitleVersion_MinorRelstep); + fprintf(title_infos_txt, "Type: 0x%02X\r\n", g_titleInfo[i].meta_key.type); + fprintf(title_infos_txt, "Install Type: 0x%02X\r\n", g_titleInfo[i].meta_key.install_type); + fprintf(title_infos_txt, "Title Size: 0x%lX\r\n", g_titleInfo[i].title_size); + + if (g_titleInfo[i].app_metadata) + { + fprintf(title_infos_txt, "Application Name: %s\r\n", g_titleInfo[i].app_metadata->lang_entry.name); + fprintf(title_infos_txt, "Application Author: %s\r\n", g_titleInfo[i].app_metadata->lang_entry.author); + fprintf(title_infos_txt, "JPEG Icon Size: 0x%X\r\n", g_titleInfo[i].app_metadata->icon_size); + + if (g_titleInfo[i].app_metadata->icon_size) + { + sprintf(icon_path, "sdmc:/records/%016lX.jpg", g_titleInfo[i].app_metadata->title_id); + icon_jpg = fopen(icon_path, "wb"); + if (icon_jpg) + { + fwrite(g_titleInfo[i].app_metadata->icon, 1, g_titleInfo[i].app_metadata->icon_size, icon_jpg); + fclose(icon_jpg); + icon_jpg = NULL; + } + } + } + + fprintf(title_infos_txt, "\r\n"); + + fflush(title_infos_txt); + } + + fclose(title_infos_txt); + title_infos_txt = NULL; + } + } + + + + + + + + + + + + + ret = g_titleInterfaceInit = true; + +end: + mutexUnlock(&g_titleMutex); + + return ret; +} + +void titleExit(void) +{ + mutexLock(&g_titleMutex); + + + + + + + + + + + + + /* Free title info. */ + if (g_titleInfo) free(g_titleInfo); + g_titleInfoCount = 0; + + /* Close eMMC System, eMMC User and SD card ncm storages. */ + titleCloseNcmStorages(); + + /* Close eMMC System, eMMC User and SD card ncm databases. */ + titleCloseNcmDatabases(); + + /* Free application metadata. */ + if (g_appMetadata) free(g_appMetadata); + g_appMetadataCount = 0; + + /* Free ns application control data. */ + if (g_nsAppControlData) free(g_nsAppControlData); + + g_titleInterfaceInit = false; + + mutexUnlock(&g_titleMutex); +} + +NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id) +{ + NcmContentMetaDatabase *ncm_db = NULL; + + switch(storage_id) + { + case NcmStorageId_GameCard: + ncm_db = &g_ncmDbGameCard; + break; + case NcmStorageId_BuiltInSystem: + ncm_db = &g_ncmDbEmmcSystem; + break; + case NcmStorageId_BuiltInUser: + ncm_db = &g_ncmDbEmmcUser; + break; + case NcmStorageId_SdCard: + ncm_db = &g_ncmDbSdCard; + break; + default: + break; + } + + return ncm_db; +} + +NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id) +{ + NcmContentStorage *ncm_storage = NULL; + + switch(storage_id) + { + case NcmStorageId_GameCard: + ncm_storage = &g_ncmStorageGameCard; + break; + case NcmStorageId_BuiltInSystem: + ncm_storage = &g_ncmStorageEmmcSystem; + break; + case NcmStorageId_BuiltInUser: + ncm_storage = &g_ncmStorageEmmcUser; + break; + case NcmStorageId_SdCard: + ncm_storage = &g_ncmStorageSdCard; + break; + default: + break; + } + + return ncm_storage; +} + + + + + + + + + + + + + + + + + + + + + + +static bool titleRetrieveApplicationMetadataFromNsRecords(void) +{ + /* Return right away if application metadata has already been retrieved. */ + if (g_appMetadata || g_appMetadataCount) return true; + + Result rc = 0; + + NsApplicationRecord *app_records = NULL; + u32 app_records_count = 0; + + bool success = false; + + /* Allocate memory for the ns application records. */ + app_records = calloc(NS_APPLICATION_RECORD_LIMIT, sizeof(NsApplicationRecord)); + if (!app_records) + { + LOGFILE("Failed to allocate memory for ns application records!"); + goto end; + } + + /* Retrieve ns application records. */ + rc = nsListApplicationRecord(app_records, NS_APPLICATION_RECORD_LIMIT, 0, (s32*)&app_records_count); + if (R_FAILED(rc)) + { + LOGFILE("nsListApplicationRecord failed! (0x%08X).", rc); + goto end; + } + + /* Return right away if no records were retrieved. */ + if (!app_records_count) + { + success = true; + goto end; + } + + /* Allocate memory for the application metadata. */ + g_appMetadata = calloc(app_records_count, sizeof(TitleApplicationMetadata)); + if (!g_appMetadata) + { + LOGFILE("Failed to allocate memory for application metadata! (%u %s).", app_records_count, app_records_count > 1 ? "entries" : "entry"); + goto end; + } + + /* Retrieve application metadata for each ns application record. */ + g_appMetadataCount = 0; + for(u32 i = 0; i < app_records_count; i++) + { + if (!titleRetrieveApplicationMetadataByTitleId(app_records[i].application_id, &(g_appMetadata[g_appMetadataCount]))) continue; + g_appMetadataCount++; + } + + /* Check retrieved application metadata count. */ + if (!g_appMetadataCount) + { + LOGFILE("Unable to retrieve application metadata from ns application records! (%u %s).", app_records_count, app_records_count > 1 ? "entries" : "entry"); + goto end; + } + + /* Decrease buffer size if needed. */ + if (g_appMetadataCount < app_records_count) + { + TitleApplicationMetadata *tmp_app_metadata = realloc(g_appMetadata, g_appMetadataCount * sizeof(TitleApplicationMetadata)); + if (!tmp_app_metadata) + { + LOGFILE("Failed to reallocate application metadata buffer! (%u %s).", g_appMetadataCount, g_appMetadataCount > 1 ? "entries" : "entry"); + goto end; + } + + g_appMetadata = tmp_app_metadata; + tmp_app_metadata = NULL; + } + + success = true; + +end: + if (!success) + { + if (g_appMetadata) + { + free(g_appMetadata); + g_appMetadata = NULL; + } + + g_appMetadataCount = 0; + } + + if (app_records) free(app_records); + + return success; +} + +static bool titleRetrieveApplicationMetadataByTitleId(u64 title_id, TitleApplicationMetadata *out) +{ + if (!g_nsAppControlData || !title_id || !out) + { + LOGFILE("Invalid parameters!"); + return false; + } + + Result rc = 0; + u64 write_size = 0; + NacpLanguageEntry *lang_entry = NULL; + + /* Retrieve ns application control data. */ + rc = nsGetApplicationControlData(NsApplicationControlSource_Storage, title_id, g_nsAppControlData, sizeof(NsApplicationControlData), &write_size); + if (R_FAILED(rc)) + { + LOGFILE("nsGetApplicationControlData failed for title ID \"%016lX\"! (0x%08X).", rc, title_id); + return false; + } + + if (write_size < sizeof(NacpStruct)) + { + LOGFILE("Retrieved application control data buffer is too small! (0x%lX).", write_size); + return false; + } + + /* Get language entry. */ + rc = nacpGetLanguageEntry(&(g_nsAppControlData->nacp), &lang_entry); + if (R_FAILED(rc)) + { + LOGFILE("nacpGetLanguageEntry failed! (0x%08X).", rc); + return false; + } + + /* Copy data. */ + out->title_id = title_id; + + memcpy(&(out->lang_entry), lang_entry, sizeof(NacpLanguageEntry)); + utilsTrimString(out->lang_entry.name); + utilsTrimString(out->lang_entry.author); + + out->icon_size = (write_size - sizeof(NacpStruct)); + memcpy(out->icon, g_nsAppControlData->icon, sizeof(g_nsAppControlData->icon)); + + return true; +} + +static bool titleOpenNcmDatabases(void) +{ + Result rc = 0; + NcmContentMetaDatabase *ncm_db = NULL; + + for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) + { + /* Retrieve ncm database pointer. */ + ncm_db = titleGetNcmDatabaseByStorageId(i); + if (!ncm_db) + { + LOGFILE("Failed to retrieve ncm database pointer for storage ID %u!", i); + return false; + } + + /* Check if the ncm database handle has already been retrieved. */ + if (serviceIsActive(&(ncm_db->s))) continue; + + /* Open ncm database. */ + rc = ncmOpenContentMetaDatabase(ncm_db, i); + if (R_FAILED(rc)) + { + /* If the SD card is mounted, but it isn't currently being used by HOS, 0x21005 will be returned, so we'll just filter this particular error and continue. */ + /* This can occur when using the "Nintendo" directory from a different console, or when the "sdmc:/Nintendo/Contents/private" file is corrupted. */ + LOGFILE("ncmOpenContentMetaDatabase failed for storage ID %u! (0x%08X).", i, rc); + if (i == NcmStorageId_SdCard && rc == 0x21005) continue; + return false; + } + } + + return true; +} + +static void titleCloseNcmDatabases(void) +{ + NcmContentMetaDatabase *ncm_db = NULL; + + for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) + { + /* Retrieve ncm database pointer. */ + ncm_db = titleGetNcmDatabaseByStorageId(i); + if (!ncm_db) continue; + + /* Check if the ncm database handle has already been retrieved. */ + if (serviceIsActive(&(ncm_db->s))) ncmContentMetaDatabaseClose(ncm_db); + } +} + +static bool titleOpenNcmStorages(void) +{ + Result rc = 0; + NcmContentStorage *ncm_storage = NULL; + + for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) + { + /* Retrieve ncm storage pointer. */ + ncm_storage = titleGetNcmStorageByStorageId(i); + if (!ncm_storage) + { + LOGFILE("Failed to retrieve ncm storage pointer for storage ID %u!", i); + return false; + } + + /* Check if the ncm storage handle has already been retrieved. */ + if (serviceIsActive(&(ncm_storage->s))) continue; + + /* Open ncm storage. */ + rc = ncmOpenContentStorage(ncm_storage, i); + if (R_FAILED(rc)) + { + /* If the SD card is mounted, but it isn't currently being used by HOS, 0x21005 will be returned, so we'll just filter this particular error and continue. */ + /* This can occur when using the "Nintendo" directory from a different console, or when the "sdmc:/Nintendo/Contents/private" file is corrupted. */ + LOGFILE("ncmOpenContentStorage failed for storage ID %u! (0x%08X).", i, rc); + if (i == NcmStorageId_SdCard && rc == 0x21005) continue; + return false; + } + } + + return true; +} + +static void titleCloseNcmStorages(void) +{ + NcmContentStorage *ncm_storage = NULL; + + for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) + { + /* Retrieve ncm storage pointer. */ + ncm_storage = titleGetNcmStorageByStorageId(i); + if (!ncm_storage) continue; + + /* Check if the ncm storage handle has already been retrieved. */ + if (serviceIsActive(&(ncm_storage->s))) ncmContentStorageClose(ncm_storage); + } +} + +static bool titleLoadTitleInfo(void) +{ + /* Return right away if title info has already been retrieved. */ + if (g_titleInfo || g_titleInfoCount) return true; + + NcmContentMetaDatabase *ncm_db = NULL; + + g_titleInfoCount = 0; + + for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) + { + /* Retrieve ncm database pointer. */ + ncm_db = titleGetNcmDatabaseByStorageId(i); + if (!ncm_db) continue; + + /* Check if the ncm database handle has already been retrieved. */ + if (!serviceIsActive(&(ncm_db->s))) continue; + + /* Retrieve content meta keys from this ncm database. */ + if (!titleRetrieveContentMetaKeysFromDatabase(i, ncm_db)) + { + LOGFILE("Failed to retrieve content meta keys from storage ID %u!", i); + return false; + } + } + + return true; +} + +static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id, NcmContentMetaDatabase *ncm_db) +{ + if (storage_id < NcmStorageId_GameCard || storage_id > NcmStorageId_SdCard || !ncm_db || !serviceIsActive(&(ncm_db->s))) + { + LOGFILE("Invalid parameters!"); + return false; + } + + Result rc = 0; + + u32 written = 0, total = 0; + NcmContentMetaKey *meta_keys = NULL, *meta_keys_tmp = NULL; + size_t meta_keys_size = sizeof(NcmContentMetaKey); + + TitleInfo *tmp_title_info = NULL; + + bool success = false; + + /* Allocate memory for the ncm application content meta keys. */ + meta_keys = calloc(1, meta_keys_size); + if (!meta_keys) + { + LOGFILE("Unable to allocate memory for the ncm application meta keys!"); + goto end; + } + + /* Get a full list of all titles available in this storage. */ + /* Meta type '0' means all title types will be retrieved. */ + rc = ncmContentMetaDatabaseList(ncm_db, (s32*)&total, (s32*)&written, meta_keys, 1, 0, 0, 0, -1, NcmContentInstallType_Full); + if (R_FAILED(rc)) + { + LOGFILE("ncmContentMetaDatabaseList failed! (0x%08X) (first entry).", rc); + goto end; + } + + /* Check if our application meta keys buffer was actually filled. */ + /* If it wasn't, odds are there are no titles in this storage. */ + if (!written || !total) + { + success = true; + goto end; + } + + /* Check if we need to resize our application meta keys buffer. */ + if (total > written) + { + /* Update application meta keys buffer size. */ + meta_keys_size *= total; + + /* Reallocate application meta keys buffer. */ + meta_keys_tmp = realloc(meta_keys, meta_keys_size); + if (!meta_keys_tmp) + { + LOGFILE("Unable to reallocate application meta keys buffer! (%u entries).", total); + goto end; + } + + meta_keys = meta_keys_tmp; + meta_keys_tmp = NULL; + + /* Issue call again. */ + rc = ncmContentMetaDatabaseList(ncm_db, (s32*)&total, (s32*)&written, meta_keys, (s32)total, 0, 0, 0, -1, NcmContentInstallType_Full); + if (R_FAILED(rc)) + { + LOGFILE("ncmContentMetaDatabaseList failed! (0x%08X) (%u %s).", rc, total, total > 1 ? "entries" : "entry"); + goto end; + } + + /* Safety check. */ + if (written != total) + { + LOGFILE("Application meta key count mismatch! (%u != %u).", written, total); + goto end; + } + } + + /* Reallocate title info buffer. */ + /* If g_titleInfo == NULL, realloc() will essentially act as a malloc(). */ + tmp_title_info = realloc(g_titleInfo, (g_titleInfoCount + total) * sizeof(TitleInfo)); + if (!tmp_title_info) + { + LOGFILE("Unable to reallocate title info buffer! (%u %s).", g_titleInfoCount + total, (g_titleInfoCount + total) > 1 ? "entries" : "entry"); + goto end; + } + + g_titleInfo = tmp_title_info; + tmp_title_info = NULL; + + /* Clear new title info buffer area. */ + memset(g_titleInfo + g_titleInfoCount, 0, total * sizeof(TitleInfo)); + + /* Fill new title info entries. */ + for(u32 i = 0; i < total; i++) + { + TitleInfo *cur_title_info = &(g_titleInfo[g_titleInfoCount + i]); + + cur_title_info->storage_id = storage_id; + memcpy(&(cur_title_info->meta_key), &(meta_keys[i]), sizeof(NcmContentMetaKey)); + /* TO DO: RETRIEVE TITLE SIZE HERE. */ + cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(meta_keys[i].id); + } + + /* Update title info count. */ + g_titleInfoCount += total; + + success = true; + +end: + if (meta_keys) free(meta_keys); + + return success; +} + +NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id) +{ + if (!g_appMetadata || !g_appMetadataCount || !title_id) return NULL; + + for(u32 i = 0; i < g_appMetadataCount; i++) + { + if (g_appMetadata[i].title_id == title_id) return &(g_appMetadata[i]); + } + + return NULL; +} diff --git a/source/title.h b/source/title.h new file mode 100644 index 0000000..c15b584 --- /dev/null +++ b/source/title.h @@ -0,0 +1,93 @@ +/* + * title.h + * + * Copyright (c) 2020, DarkMatterCore . + * + * This file is part of nxdumptool (https://github.com/DarkMatterCore/nxdumptool). + * + * nxdumptool 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. + * + * nxdumptool 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 __TITLE_H__ +#define __TITLE_H__ + +#define TITLE_PATCH_ID_MASK (u64)0x800 +#define TITLE_ADDONCONTENT_ID_MASK (u64)0xFFFFFFFFFFFF0000 + +typedef struct { + u32 TitleVersion_MinorRelstep : 8; + u32 TitleVersion_MajorRelstep : 8; + u32 TitleVersion_Micro : 4; + u32 TitleVersion_Minor : 6; + u32 TitleVersion_Major : 6; +} TitleVersion; + +/// Retrieved from ns application records. +typedef struct { + u64 title_id; ///< Title ID from the application this data belongs to. + NacpLanguageEntry lang_entry; ///< UTF-8 strings in the language set in the console settings. + u32 icon_size; ///< JPEG icon size. + u8 icon[0x20000]; ///< JPEG icon data. +} TitleApplicationMetadata; + +/// Retrieved from ncm databases. +typedef struct { + u8 storage_id; ///< NcmStorageId. + NcmContentMetaKey meta_key; ///< Used with ncm calls. + u64 title_size; ///< Total title size. + TitleApplicationMetadata *app_metadata; ///< Not available for all titles. +} TitleInfo; + + + + + + + + + +bool titleInitialize(void); +void titleExit(void); + +NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id); +NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id); + + + + + +/// Miscellaneous functions. + +NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id) +{ + return (app_id | TITLE_PATCH_ID_MASK); +} + +NX_INLINE u64 titleGetApplicationIdByPatchId(u64 patch_id) +{ + return (patch_id & ~TITLE_PATCH_ID_MASK); +} + +NX_INLINE bool titleCheckIfPatchIdBelongsToApplicationId(u64 app_id, u64 patch_id) +{ + return (titleGetPatchIdByApplicationId(app_id) == patch_id); +} + +NX_INLINE bool titleCheckIfAddOnContentIdBelongsToApplicationId(u64 app_id, u64 aoc_id) +{ + return ((app_id & TITLE_ADDONCONTENT_ID_MASK) == (aoc_id & TITLE_ADDONCONTENT_ID_MASK)); +} + +#endif /* __TITLE_H__ */ diff --git a/source/utils.c b/source/utils.c index 1972224..30fe29d 100644 --- a/source/utils.c +++ b/source/utils.c @@ -27,6 +27,7 @@ #include "services.h" #include "nca.h" #include "usb.h" +#include "title.h" #include "fatfs/ff.h" #define LOGFILE_PATH "./" APP_TITLE ".log" @@ -105,6 +106,13 @@ bool utilsInitializeResources(void) goto end; } + /* Initialize title interface. */ + if (!titleInitialize()) + { + LOGFILE("Failed to initialize the title interface!"); + goto end; + } + /* Retrieve SD card FsFileSystem element. */ if (!(g_sdCardFileSystem = fsdevGetDeviceFileSystem("sdmc:"))) { @@ -148,8 +156,6 @@ void utilsCloseResources(void) { mutexLock(&g_resourcesMutex); - if (!g_resourcesInitialized) goto end; - /* Free LVGL resources. */ //lvglHelperExit(); @@ -171,6 +177,9 @@ void utilsCloseResources(void) /* Unmount eMMC BIS System partition. */ utilsUnmountEmmcBisSystemPartitionStorage(); + /* Deinitialize title interface. */ + titleExit(); + /* Deinitialize gamecard interface. */ gamecardExit(); @@ -185,7 +194,6 @@ void utilsCloseResources(void) g_resourcesInitialized = false; -end: mutexUnlock(&g_resourcesMutex); } diff --git a/source/utils.h b/source/utils.h index 6945816..0762b63 100644 --- a/source/utils.h +++ b/source/utils.h @@ -33,6 +33,7 @@ #include #include #include +#include #include #include #include diff --git a/todo.txt b/todo.txt index b32fba5..558a624 100644 --- a/todo.txt +++ b/todo.txt @@ -9,6 +9,8 @@ todo: gamecard: hfs0 filelist generation functions + mem: find a better way to block fs calls while debugging fs + pfs0: filelist generation functions pfs0: full header aligned to 0x20 (nsp)