1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-22 18:26:39 +00:00

title: use dynamic pointer arrays.

* Added functions to deal with title info and application metadata reallocations, greatly reducing the number of references to realloc() throughout the code.

* Tweaked gamecardGetHashFileSystemContext() to not return a pointer to a dynamic context.

* Added a type field to Hash FS contexts.
This commit is contained in:
Pablo Curiel 2021-03-09 21:12:01 -04:00
parent 8be5460229
commit a01b7846de
7 changed files with 438 additions and 235 deletions

View file

@ -349,7 +349,7 @@ static void waitForGameCardAndUsb(void)
while(true) while(true)
{ {
if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && !titleIsGameCardInfoUpdated()) break; if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && titleIsGameCardInfoUpdated()) break;
} }
consolePrint("waiting for usb session...\n"); consolePrint("waiting for usb session...\n");

View file

@ -356,55 +356,57 @@ bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out)
return ret; return ret;
} }
HashFileSystemContext *gamecardGetHashFileSystemContext(u8 hfs_partition_type) bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out)
{ {
HashFileSystemContext *fs_ctx = NULL, *fs_ctx_copy = NULL; if (hfs_partition_type >= GameCardHashFileSystemPartitionType_Count || !out)
{
LOG_MSG("Invalid parameters!");
return false;
}
HashFileSystemContext *fs_ctx = NULL;
bool success = false; bool success = false;
mutexLock(&g_gameCardMutex); mutexLock(&g_gameCardMutex);
/* Free Hash FS context. */
hfsFreeContext(out);
/* Get pointer to the Hash FS context for the requested partition. */ /* Get pointer to the Hash FS context for the requested partition. */
fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type); fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type);
if (!fs_ctx) goto end; if (!fs_ctx) goto end;
/* Create a copy of the retrieved Hash FS context. */ /* Fill Hash FS context. */
/* We won't return a pointer to any of our internal Hash FS contexts (for concurrency reasons). */ out->name = strdup(fs_ctx->name);
fs_ctx_copy = calloc(1, sizeof(HashFileSystemContext)); if (!out->name)
if (!fs_ctx_copy)
{
LOG_MSG("Unable to allocate memory for Hash FS context! (%s).", fs_ctx->name);
goto end;
}
fs_ctx_copy->name = strdup(fs_ctx->name);
if (!fs_ctx_copy->name)
{ {
LOG_MSG("Failed to duplicate Hash FS partition name! (%s).", fs_ctx->name); LOG_MSG("Failed to duplicate Hash FS partition name! (%s).", fs_ctx->name);
goto end; goto end;
} }
fs_ctx_copy->offset = fs_ctx->offset; out->type = fs_ctx->type;
fs_ctx_copy->size = fs_ctx->size; out->offset = fs_ctx->offset;
fs_ctx_copy->header_size = fs_ctx->header_size; out->size = fs_ctx->size;
out->header_size = fs_ctx->header_size;
fs_ctx_copy->header = calloc(fs_ctx->header_size, sizeof(u8)); out->header = calloc(fs_ctx->header_size, sizeof(u8));
if (!fs_ctx_copy->header) if (!out->header)
{ {
LOG_MSG("Failed to duplicate Hash FS partition header! (%s).", fs_ctx->name); LOG_MSG("Failed to duplicate Hash FS partition header! (%s).", fs_ctx->name);
goto end; goto end;
} }
memcpy(fs_ctx_copy->header, fs_ctx->header, fs_ctx->header_size); memcpy(out->header, fs_ctx->header, fs_ctx->header_size);
/* Update flag. */ /* Update flag. */
success = true; success = true;
end: end:
if (!success) hfsFreeContext(&fs_ctx_copy); if (!success) hfsFreeContext(out);
mutexUnlock(&g_gameCardMutex); mutexUnlock(&g_gameCardMutex);
return fs_ctx_copy; return success;
} }
bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size) bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size)
@ -622,7 +624,11 @@ end:
{ {
if (dump_gamecard_header) LOG_DATA(&g_gameCardHeader, sizeof(GameCardHeader), "Gamecard header dump:"); if (dump_gamecard_header) LOG_DATA(&g_gameCardHeader, sizeof(GameCardHeader), "Gamecard header dump:");
if (!g_gameCardHfsCtx) hfsFreeContext(&root_fs_ctx); if (!g_gameCardHfsCtx && root_fs_ctx)
{
hfsFreeContext(root_fs_ctx);
free(root_fs_ctx);
}
gamecardFreeInfo(); gamecardFreeInfo();
} }
@ -638,7 +644,15 @@ static void gamecardFreeInfo(void)
if (g_gameCardHfsCtx) if (g_gameCardHfsCtx)
{ {
for(u32 i = 0; i < g_gameCardHfsCount; i++) hfsFreeContext(&(g_gameCardHfsCtx[i])); for(u32 i = 0; i < g_gameCardHfsCount; i++)
{
HashFileSystemContext *cur_fs_ctx = g_gameCardHfsCtx[i];
if (cur_fs_ctx)
{
hfsFreeContext(cur_fs_ctx);
free(cur_fs_ctx);
}
}
free(g_gameCardHfsCtx); free(g_gameCardHfsCtx);
g_gameCardHfsCtx = NULL; g_gameCardHfsCtx = NULL;
@ -952,7 +966,7 @@ NX_INLINE u64 gamecardGetCapacityFromRomSizeValue(u8 rom_size)
static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char *name, u64 offset, u64 size, u8 *hash, u64 hash_target_offset, u32 hash_target_size) static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char *name, u64 offset, u64 size, u8 *hash, u64 hash_target_offset, u32 hash_target_size)
{ {
u32 magic = 0; u32 i = 0, magic = 0;
HashFileSystemContext *fs_ctx = NULL; HashFileSystemContext *fs_ctx = NULL;
HashFileSystemHeader fs_header = {0}; HashFileSystemHeader fs_header = {0};
u8 fs_header_hash[SHA256_HASH_SIZE] = {0}; u8 fs_header_hash[SHA256_HASH_SIZE] = {0};
@ -982,6 +996,20 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char
goto end; goto end;
} }
/* Determine Hash FS partition type. */
for(i = GameCardHashFileSystemPartitionType_Root; i < GameCardHashFileSystemPartitionType_Count; i++)
{
if (!strcmp(g_gameCardHfsPartitionNames[i], fs_ctx->name)) break;
}
if (i >= GameCardHashFileSystemPartitionType_Count)
{
LOG_MSG("Failed to find a matching Hash FS partition type for \"%s\"! (offset 0x%lX).", fs_ctx->name, offset);
goto end;
}
fs_ctx->type = i;
/* Read partial Hash FS header. */ /* Read partial Hash FS header. */
if (!gamecardReadStorageArea(&fs_header, sizeof(HashFileSystemHeader), offset, false)) if (!gamecardReadStorageArea(&fs_header, sizeof(HashFileSystemHeader), offset, false))
{ {

View file

@ -225,10 +225,9 @@ bool gamecardGetRomCapacity(u64 *out);
/// Fills the provided VersionType1 pointer with the bundled firmware update version in the inserted gamecard. /// Fills the provided VersionType1 pointer with the bundled firmware update version in the inserted gamecard.
bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out); bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out);
/// Returns a pointer to a dynamically-allocated HashFileSystemContext for the provided Hash FS partition type. /// 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 returned context. /// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the underlying data from the filled context.
/// Returns NULL if an error occurs. bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out);
HashFileSystemContext *gamecardGetHashFileSystemContext(u8 hfs_partition_type);
/// One-shot function to retrieve meaningful information from a Hash FS entry by name without using gamecardGetHashFileSystemContext() + Hash FS functions. /// One-shot function to retrieve meaningful information from a Hash FS entry by name without using gamecardGetHashFileSystemContext() + Hash FS functions.
/// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer. The returned offset is always relative to the start of the gamecard image. /// 'out_offset' or 'out_size' may be set to NULL, but at least one of them must be a valid pointer. The returned offset is always relative to the start of the gamecard image.

View file

@ -44,6 +44,7 @@ typedef struct {
/// Internally used by gamecard functions. /// Internally used by gamecard functions.
/// Use gamecardGetHashFileSystemContext() to retrieve a Hash FS context. /// Use gamecardGetHashFileSystemContext() to retrieve a Hash FS context.
typedef struct { typedef struct {
u8 type; ///< GameCardHashFileSystemPartitionType.
char *name; ///< Dynamically allocated partition name. char *name; ///< Dynamically allocated partition name.
u64 offset; ///< Partition offset (relative to the start of gamecard image). u64 offset; ///< Partition offset (relative to the start of gamecard image).
u64 size; ///< Partition size. u64 size; ///< Partition size.
@ -67,15 +68,12 @@ bool hfsGetTotalDataSize(HashFileSystemContext *ctx, u64 *out_size);
/// Miscellaneous functions. /// Miscellaneous functions.
NX_INLINE void hfsFreeContext(HashFileSystemContext **ctx) NX_INLINE void hfsFreeContext(HashFileSystemContext *ctx)
{ {
if (!ctx || !*ctx) return; if (!ctx) return;
if (ctx->name) free(ctx->name);
if ((*ctx)->name) free((*ctx)->name); if (ctx->header) free(ctx->header);
if ((*ctx)->header) free((*ctx)->header); memset(ctx, 0, sizeof(HashFileSystemContext));
free(*ctx);
*ctx = NULL;
} }
NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx) NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx)

View file

@ -133,7 +133,7 @@ static bool memRetrieveProgramMemory(MemoryLocation *location, bool is_segment)
mem_type != MemType_Io && mem_type != MemType_ThreadLocal && mem_type != MemType_Reserved))) || (is_segment && (mem_type == MemType_CodeStatic || mem_type == MemType_CodeMutable) && \ mem_type != MemType_Io && mem_type != MemType_ThreadLocal && mem_type != MemType_Reserved))) || (is_segment && (mem_type == MemType_CodeStatic || mem_type == MemType_CodeMutable) && \
(((segment <<= 1) >> 1) & location->mask)))) (((segment <<= 1) >> 1) & location->mask))))
{ {
/* If location->data == NULL, realloc() will essentially act as a malloc(). */ /* Reallocate data buffer. */
tmp = realloc(location->data, location->data_size + mem_info.size); tmp = realloc(location->data, location->data_size + mem_info.size);
if (!tmp) if (!tmp)
{ {

View file

@ -42,13 +42,13 @@ static bool g_titleInterfaceInit = false, g_titleGameCardInfoThreadCreated = fal
static NsApplicationControlData *g_nsAppControlData = NULL; static NsApplicationControlData *g_nsAppControlData = NULL;
static TitleApplicationMetadata *g_appMetadata = NULL; static TitleApplicationMetadata **g_appMetadata = NULL;
static u32 g_appMetadataCount = 0; static u32 g_appMetadataCount = 0;
static NcmContentMetaDatabase g_ncmDbGameCard = {0}, g_ncmDbEmmcSystem = {0}, g_ncmDbEmmcUser = {0}, g_ncmDbSdCard = {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 NcmContentStorage g_ncmStorageGameCard = {0}, g_ncmStorageEmmcSystem = {0}, g_ncmStorageEmmcUser = {0}, g_ncmStorageSdCard = {0};
static TitleInfo *g_titleInfo = NULL; static TitleInfo **g_titleInfo = NULL;
static u32 g_titleInfoCount = 0, g_titleInfoGameCardStartIndex = 0, g_titleInfoGameCardCount = 0, g_titleInfoOrphanCount = 0; static u32 g_titleInfoCount = 0, g_titleInfoGameCardStartIndex = 0, g_titleInfoGameCardCount = 0, g_titleInfoOrphanCount = 0;
static const char *g_titleNcmContentTypeNames[] = { static const char *g_titleNcmContentTypeNames[] = {
@ -380,7 +380,10 @@ static const u32 g_systemTitlesCount = MAX_ELEMENTS(g_systemTitles);
/* Function prototypes. */ /* Function prototypes. */
NX_INLINE void titleFreeApplicationMetadata(void); NX_INLINE void titleFreeApplicationMetadata(void);
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool free_entries);
NX_INLINE void titleFreeTitleInfo(void); NX_INLINE void titleFreeTitleInfo(void);
static bool titleReallocateTitleInfo(u32 extra_title_count, bool free_entries);
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id); NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id);
@ -588,7 +591,7 @@ TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u3
u32 app_count = 0; u32 app_count = 0;
TitleApplicationMetadata **app_metadata = NULL, **tmp_app_metadata = NULL; TitleApplicationMetadata **app_metadata = NULL, **tmp_app_metadata = NULL;
if (!g_titleInterfaceInit || !g_appMetadata || (is_system && g_appMetadataCount < g_systemTitlesCount) || (!is_system && g_appMetadataCount == g_systemTitlesCount) || !out_count) if (!g_titleInterfaceInit || !g_appMetadata || !*g_appMetadata || (is_system && g_appMetadataCount < g_systemTitlesCount) || (!is_system && g_appMetadataCount == g_systemTitlesCount) || !out_count)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
goto end; goto end;
@ -596,17 +599,17 @@ TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u3
for(u32 i = start_idx; i < max_val; i++) for(u32 i = start_idx; i < max_val; i++)
{ {
TitleApplicationMetadata *cur_app_metadata = &(g_appMetadata[i]); TitleApplicationMetadata *cur_app_metadata = g_appMetadata[i];
/* Skip current metadata entry if content data for this title isn't available. */ /* Skip current metadata entry if content data for this title isn't available. */
if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id, false)) || \ if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id, false)) || \
(!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue; (!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue;
/* Reallocate pointer buffer. */ /* Reallocate application metadata pointer array. */
tmp_app_metadata = realloc(app_metadata, (app_count + 1) * sizeof(TitleApplicationMetadata*)); tmp_app_metadata = realloc(app_metadata, (app_count + 1) * sizeof(TitleApplicationMetadata*));
if (!tmp_app_metadata) if (!tmp_app_metadata)
{ {
LOG_MSG("Failed to reallocate application metadata pointer buffer!"); LOG_MSG("Failed to reallocate application metadata pointer array!");
if (app_metadata) free(app_metadata); if (app_metadata) free(app_metadata);
app_metadata = NULL; app_metadata = NULL;
goto end; goto end;
@ -641,7 +644,7 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out)
bool success = false; bool success = false;
if (!g_titleInterfaceInit || !out) if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !app_id || !out)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
goto end; goto end;
@ -659,8 +662,8 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out)
/* Get first add-on content title info. */ /* Get first add-on content title info. */
for(u32 i = 0; i < g_titleInfoCount; i++) for(u32 i = 0; i < g_titleInfoCount; i++)
{ {
TitleInfo *title_info = &(g_titleInfo[i]); TitleInfo *title_info = g_titleInfo[i];
if (title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id)) if (title_info && title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id))
{ {
out->aoc_info = title_info; out->aoc_info = title_info;
break; break;
@ -695,25 +698,25 @@ TitleInfo **titleGetInfoFromOrphanTitles(u32 *out_count)
TitleInfo **orphan_info = NULL; TitleInfo **orphan_info = NULL;
if (!g_titleInterfaceInit || !g_titleInfo || !g_titleInfoCount || !g_titleInfoOrphanCount || !out_count) if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_titleInfoOrphanCount || !out_count)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
goto end; goto end;
} }
/* Allocate orphan title info buffer. */ /* Allocate orphan title info pointer array. */
orphan_info = calloc(g_titleInfoOrphanCount, sizeof(TitleInfo*)); orphan_info = calloc(g_titleInfoOrphanCount, sizeof(TitleInfo*));
if (!orphan_info) if (!orphan_info)
{ {
LOG_MSG("Failed to allocate memory for orphan title info buffer!"); LOG_MSG("Failed to allocate memory for orphan title info pointer array!");
goto end; goto end;
} }
/* Get pointers to orphan title info entries. */ /* Get pointers to orphan title info entries. */
for(u32 i = 0, j = 0; i < g_titleInfoCount && j < g_titleInfoOrphanCount; i++) for(u32 i = 0, j = 0; i < g_titleInfoCount && j < g_titleInfoOrphanCount; i++)
{ {
TitleInfo *title_info = &(g_titleInfo[i]); TitleInfo *title_info = g_titleInfo[i];
if (title_info->meta_key.type != NcmContentMetaType_Application && !title_info->app_metadata) orphan_info[j++] = title_info; if (title_info && title_info->meta_key.type != NcmContentMetaType_Application && !title_info->app_metadata) orphan_info[j++] = title_info;
} }
/* Sort orphan title info entries by title ID. */ /* Sort orphan title info entries by title ID. */
@ -801,7 +804,7 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
char *filename = NULL, *tmp_filename = NULL; char *filename = NULL, *tmp_filename = NULL;
char app_name[0x400] = {0}; char app_name[0x400] = {0};
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || !g_titleInfo || !g_titleInfoCount || !g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || \ if (!g_titleInterfaceInit || !g_titleGameCardAvailable || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || \
g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount) || name_convention > TitleFileNameConvention_IdAndVersionOnly || \ g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount) || name_convention > TitleFileNameConvention_IdAndVersionOnly || \
(name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) (name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
{ {
@ -811,10 +814,10 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++) for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++)
{ {
TitleInfo *app_info = &(g_titleInfo[i]); TitleInfo *app_info = g_titleInfo[i];
u32 app_version = app_info->meta_key.version; if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue;
if (app_info->meta_key.type != NcmContentMetaType_Application) continue; u32 app_version = app_info->meta_key.version;
/* Check if the inserted gamecard holds any bundled patches for the current user application. */ /* Check if the inserted gamecard holds any bundled patches for the current user application. */
/* If so, we'll use the highest patch version available as part of the filename. */ /* If so, we'll use the highest patch version available as part of the filename. */
@ -822,8 +825,8 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
{ {
if (j == i) continue; if (j == i) continue;
TitleInfo *patch_info = &(g_titleInfo[j]); TitleInfo *patch_info = g_titleInfo[j];
if (patch_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, patch_info->meta_key.id) || \ if (!patch_info || patch_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, patch_info->meta_key.id) || \
patch_info->meta_key.version <= app_version) continue; patch_info->meta_key.version <= app_version) continue;
app_version = patch_info->meta_key.version; app_version = patch_info->meta_key.version;
@ -900,7 +903,12 @@ NX_INLINE void titleFreeApplicationMetadata(void)
{ {
for(u32 i = 0; i < g_appMetadataCount; i++) for(u32 i = 0; i < g_appMetadataCount; i++)
{ {
if (g_appMetadata[i].icon) free(g_appMetadata[i].icon); TitleApplicationMetadata *cur_app_metadata = g_appMetadata[i];
if (cur_app_metadata)
{
if (cur_app_metadata->icon) free(cur_app_metadata->icon);
free(cur_app_metadata);
}
} }
free(g_appMetadata); free(g_appMetadata);
@ -910,13 +918,74 @@ NX_INLINE void titleFreeApplicationMetadata(void)
g_appMetadataCount = 0; g_appMetadataCount = 0;
} }
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool free_entries)
{
if (free_entries && !g_appMetadata)
{
LOG_MSG("Invalid parameters!");
return false;
}
TitleApplicationMetadata **tmp_app_metadata = NULL;
u32 realloc_app_count = (!free_entries ? (g_appMetadataCount + extra_app_count) : g_appMetadataCount);
bool success = false;
if (free_entries)
{
/* Free previously allocated application metadata entries. */
for(u32 i = 0; i <= extra_app_count; i++)
{
TitleApplicationMetadata *cur_app_metadata = g_appMetadata[g_appMetadataCount + i];
if (cur_app_metadata)
{
if (cur_app_metadata->icon) free(cur_app_metadata->icon);
free(cur_app_metadata);
cur_app_metadata = NULL;
}
}
}
if (realloc_app_count)
{
/* Reallocate application metadata pointer array. */
tmp_app_metadata = realloc(g_appMetadata, realloc_app_count * sizeof(TitleApplicationMetadata*));
if (tmp_app_metadata)
{
/* Update application metadata pointer. */
g_appMetadata = tmp_app_metadata;
tmp_app_metadata = NULL;
/* Clear new application metadata pointer array area (if needed). */
if (!free_entries && extra_app_count) memset(g_appMetadata + g_appMetadataCount, 0, extra_app_count * sizeof(TitleApplicationMetadata*));
} else {
LOG_MSG("Failed to reallocate application metadata pointer array! (%u element[s]).", realloc_app_count);
goto end;
}
} else {
/* Free application metadata pointer array. */
free(g_appMetadata);
g_appMetadata = NULL;
}
/* Update flag. */
success = true;
end:
return success;
}
NX_INLINE void titleFreeTitleInfo(void) NX_INLINE void titleFreeTitleInfo(void)
{ {
if (g_titleInfo) if (g_titleInfo)
{ {
for(u32 i = 0; i < g_titleInfoCount; i++) for(u32 i = 0; i < g_titleInfoCount; i++)
{ {
if (g_titleInfo[i].content_infos) free(g_titleInfo[i].content_infos); TitleInfo *cur_title_info = g_titleInfo[i];
if (cur_title_info)
{
if (cur_title_info->content_infos) free(cur_title_info->content_infos);
free(cur_title_info);
}
} }
free(g_titleInfo); free(g_titleInfo);
@ -926,13 +995,70 @@ NX_INLINE void titleFreeTitleInfo(void)
g_titleInfoCount = g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = g_titleInfoOrphanCount = 0; g_titleInfoCount = g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = g_titleInfoOrphanCount = 0;
} }
static bool titleReallocateTitleInfo(u32 extra_title_count, bool free_entries)
{
if (free_entries && !g_titleInfo)
{
LOG_MSG("Invalid parameters!");
return false;
}
TitleInfo **tmp_title_info = NULL;
u32 realloc_title_count = (!free_entries ? (g_titleInfoCount + extra_title_count) : g_titleInfoCount);
bool success = false;
if (free_entries)
{
/* Free previously allocated title info entries. */
for(u32 i = 0; i <= extra_title_count; i++)
{
TitleInfo *cur_title_info = g_titleInfo[g_titleInfoCount + i];
if (cur_title_info)
{
if (cur_title_info->content_infos) free(cur_title_info->content_infos);
free(cur_title_info);
cur_title_info = NULL;
}
}
}
if (realloc_title_count)
{
/* Reallocate title info pointer array. */
tmp_title_info = realloc(g_titleInfo, realloc_title_count * sizeof(TitleInfo*));
if (tmp_title_info)
{
/* Update title info pointer. */
g_titleInfo = tmp_title_info;
tmp_title_info = NULL;
/* Clear new title info pointer array area (if needed). */
if (!free_entries && extra_title_count) memset(g_titleInfo + g_titleInfoCount, 0, extra_title_count * sizeof(TitleInfo*));
} else {
LOG_MSG("Failed to reallocate title info pointer array! (%u element[s]).", realloc_title_count);
goto end;
}
} else {
/* Free title info pointer array. */
free(g_titleInfo);
g_titleInfo = NULL;
}
/* Update flag. */
success = true;
end:
return success;
}
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id) NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id)
{ {
if (!g_appMetadata || !g_appMetadataCount || !title_id) return NULL; if (!g_appMetadata || !*g_appMetadata || !g_appMetadataCount || !title_id) return NULL;
for(u32 i = 0; i < g_appMetadataCount; i++) for(u32 i = 0; i < g_appMetadataCount; i++)
{ {
if (g_appMetadata[i].title_id == title_id) return &(g_appMetadata[i]); TitleApplicationMetadata *cur_app_metadata = g_appMetadata[i];
if (cur_app_metadata->title_id == title_id) return cur_app_metadata;
} }
return NULL; return NULL;
@ -940,40 +1066,50 @@ NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 ti
static bool titleGenerateMetadataEntriesFromSystemTitles(void) static bool titleGenerateMetadataEntriesFromSystemTitles(void)
{ {
TitleApplicationMetadata *tmp_app_metadata = NULL; u32 extra_app_count = 0;
bool success = false;
/* Reallocate application metadata buffer. */ /* Reallocate application metadata pointer array. */
/* If g_appMetadata == NULL, realloc() will essentially act as a malloc(). */ if (!titleReallocateApplicationMetadata(g_systemTitlesCount, false))
tmp_app_metadata = realloc(g_appMetadata, (g_appMetadataCount + g_systemTitlesCount) * sizeof(TitleApplicationMetadata));
if (!tmp_app_metadata)
{ {
LOG_MSG("Failed to reallocate application metadata buffer! (%u %s).", g_appMetadataCount + g_systemTitlesCount, (g_appMetadataCount + g_systemTitlesCount) > 1 ? "entries" : "entry"); LOG_MSG("Failed to reallocate application metadata pointer array for system titles!");
return false; return false;
} }
g_appMetadata = tmp_app_metadata;
tmp_app_metadata = NULL;
/* Clear new application metadata buffer area. */
memset(g_appMetadata + g_appMetadataCount, 0, g_systemTitlesCount * sizeof(TitleApplicationMetadata));
/* Fill new application metadata entries. */ /* Fill new application metadata entries. */
for(u32 i = 0; i < g_systemTitlesCount; i++) for(extra_app_count = 0; extra_app_count < g_systemTitlesCount; extra_app_count++)
{ {
TitleApplicationMetadata *app_metadata = &(g_appMetadata[g_appMetadataCount + i]); /* Allocate memory for the current entry. */
const SystemTitleName *system_title = &(g_systemTitles[i]); TitleApplicationMetadata *cur_app_metadata = calloc(1, sizeof(TitleApplicationMetadata));
if (!cur_app_metadata)
{
LOG_MSG("Failed to allocate memory for application metadata entry #%u!", extra_app_count);
goto end;
}
app_metadata->title_id = system_title->title_id; /* Fill information. */
sprintf(app_metadata->lang_entry.name, system_title->name); const SystemTitleName *system_title = &(g_systemTitles[extra_app_count]);
cur_app_metadata->title_id = system_title->title_id;
sprintf(cur_app_metadata->lang_entry.name, system_title->name);
/* Set application metadata entry pointer. */
g_appMetadata[g_appMetadataCount + extra_app_count] = cur_app_metadata;
} }
/* Sort metadata entries by title ID. */ /* Sort metadata entries by title ID. */
qsort(g_appMetadata + g_appMetadataCount, g_systemTitlesCount, sizeof(TitleApplicationMetadata), &titleSystemTitleMetadataEntrySortFunction); if (g_systemTitlesCount > 1) qsort(g_appMetadata + g_appMetadataCount, g_systemTitlesCount, sizeof(TitleApplicationMetadata*), &titleSystemTitleMetadataEntrySortFunction);
/* Update application metadata count. */ /* Update application metadata count. */
g_appMetadataCount += g_systemTitlesCount; g_appMetadataCount += g_systemTitlesCount;
return true; /* Update flag. */
success = true;
end:
/* Free previously allocated application metadata pointers. Ignore return value. */
if (!success) titleReallocateApplicationMetadata(extra_app_count, true);
return success;
} }
static bool titleGenerateMetadataEntriesFromNsRecords(void) static bool titleGenerateMetadataEntriesFromNsRecords(void)
@ -981,22 +1117,19 @@ static bool titleGenerateMetadataEntriesFromNsRecords(void)
Result rc = 0; Result rc = 0;
NsApplicationRecord *app_records = NULL; NsApplicationRecord *app_records = NULL;
u32 app_records_count = 0; u32 app_records_count = 0, extra_app_count = 0;
u32 cur_app_count = g_appMetadataCount, new_app_count = 0; bool success = false, free_entries = false;
TitleApplicationMetadata *tmp_app_metadata = NULL;
bool success = false; /* Allocate memory for the NS application records. */
/* Allocate memory for the ns application records. */
app_records = calloc(NS_APPLICATION_RECORD_LIMIT, sizeof(NsApplicationRecord)); app_records = calloc(NS_APPLICATION_RECORD_LIMIT, sizeof(NsApplicationRecord));
if (!app_records) if (!app_records)
{ {
LOG_MSG("Failed to allocate memory for ns application records!"); LOG_MSG("Failed to allocate memory for NS application records!");
goto end; return false;
} }
/* Retrieve ns application records. */ /* Retrieve NS application records. */
rc = nsListApplicationRecord(app_records, NS_APPLICATION_RECORD_LIMIT, 0, (s32*)&app_records_count); rc = nsListApplicationRecord(app_records, NS_APPLICATION_RECORD_LIMIT, 0, (s32*)&app_records_count);
if (R_FAILED(rc)) if (R_FAILED(rc))
{ {
@ -1011,57 +1144,64 @@ static bool titleGenerateMetadataEntriesFromNsRecords(void)
goto end; goto end;
} }
/* Reallocate application metadata buffer. */ /* Reallocate application metadata pointer array. */
tmp_app_metadata = realloc(g_appMetadata, (g_appMetadataCount + app_records_count) * sizeof(TitleApplicationMetadata)); if (!titleReallocateApplicationMetadata(app_records_count, false))
if (!tmp_app_metadata)
{ {
LOG_MSG("Failed to reallocate application metadata buffer! (%u %s).", g_appMetadataCount + app_records_count, (g_appMetadataCount + app_records_count) > 1 ? "entries" : "entry"); LOG_MSG("Failed to reallocate application metadata pointer array for NS records!");
goto end; goto end;
} }
g_appMetadata = tmp_app_metadata; free_entries = true;
tmp_app_metadata = NULL;
/* Retrieve application metadata for each ns application record. */ /* Retrieve application metadata for each NS application record. */
for(u32 i = 0; i < app_records_count; i++) for(u32 i = 0; i < app_records_count; i++)
{ {
if (!titleRetrieveApplicationMetadataByTitleId(app_records[i].application_id, &(g_appMetadata[g_appMetadataCount + new_app_count]))) continue; TitleApplicationMetadata *cur_app_metadata = g_appMetadata[g_appMetadataCount + extra_app_count];
new_app_count++; if (!cur_app_metadata)
{
/* Allocate memory for a new application metadata entry. */
cur_app_metadata = calloc(1, sizeof(TitleApplicationMetadata));
if (!cur_app_metadata)
{
LOG_MSG("Failed to allocate memory for application metadata entry #%u! (%u / %u).", extra_app_count, i + 1, app_records_count);
goto end;
}
/* Set application metadata entry pointer. */
g_appMetadata[g_appMetadataCount + extra_app_count] = cur_app_metadata;
}
/* Retrieve application metadata. */
if (!titleRetrieveApplicationMetadataByTitleId(app_records[i].application_id, cur_app_metadata)) continue;
/* Increase extra application metadata counter. */
extra_app_count++;
} }
/* Check retrieved application metadata count. */ /* Check retrieved application metadata count. */
if (!new_app_count) if (!extra_app_count)
{ {
LOG_MSG("Unable to retrieve application metadata from ns application records! (%u %s).", app_records_count, app_records_count > 1 ? "entries" : "entry"); LOG_MSG("Unable to retrieve application metadata from NS application records! (%u element[s]).", app_records_count);
goto end; goto end;
} }
/* Sort application metadata entries by name. */ /* Sort application metadata entries by name. */
if (new_app_count > 1) qsort(g_appMetadata + g_appMetadataCount, new_app_count, sizeof(TitleApplicationMetadata), &titleUserApplicationMetadataEntrySortFunction); if (extra_app_count > 1) qsort(g_appMetadata + g_appMetadataCount, extra_app_count, sizeof(TitleApplicationMetadata*), &titleUserApplicationMetadataEntrySortFunction);
/* Update application metadata count. */ /* Update application metadata count. */
g_appMetadataCount += new_app_count; g_appMetadataCount += extra_app_count;
/* Free extra allocated pointers if we didn't use them. */
if (extra_app_count < app_records_count) titleReallocateApplicationMetadata(0, false);
/* Update flag. */
success = true; success = true;
end: end:
if (app_records) if (app_records) free(app_records);
{
/* Decrease application metadata buffer size if needed. */ /* Free previously allocated application metadata pointers. Ignore return value. */
if (app_records_count && g_appMetadataCount < (cur_app_count + app_records_count)) if (!success && free_entries) titleReallocateApplicationMetadata(extra_app_count, true);
{
TitleApplicationMetadata *tmp_app_metadata = realloc(g_appMetadata, g_appMetadataCount * sizeof(TitleApplicationMetadata));
if (tmp_app_metadata)
{
g_appMetadata = tmp_app_metadata;
tmp_app_metadata = NULL;
} else {
LOG_MSG("Failed to reallocate application metadata buffer! (%u %s).", g_appMetadataCount, g_appMetadataCount > 1 ? "entries" : "entry");
}
}
free(app_records);
}
return success; return success;
} }
@ -1267,7 +1407,7 @@ static void titleCloseNcmDatabaseAndStorageFromGameCard(void)
static bool titleLoadPersistentStorageTitleInfo(void) static bool titleLoadPersistentStorageTitleInfo(void)
{ {
/* Return right away if title info has already been retrieved. */ /* Return right away if title info has already been retrieved. */
if (g_titleInfo || g_titleInfoCount) return true; if ((g_titleInfo && *g_titleInfo) || g_titleInfoCount) return true;
for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++) for(u8 i = NcmStorageId_BuiltInSystem; i <= NcmStorageId_SdCard; i++)
{ {
@ -1294,13 +1434,11 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
Result rc = 0; Result rc = 0;
u32 written = 0, total = 0; u32 written = 0, total = 0, extra_title_count = 0;
NcmContentMetaKey *meta_keys = NULL, *meta_keys_tmp = NULL; NcmContentMetaKey *meta_keys = NULL, *meta_keys_tmp = NULL;
size_t meta_keys_size = sizeof(NcmContentMetaKey); size_t meta_keys_size = sizeof(NcmContentMetaKey);
TitleInfo *tmp_title_info = NULL; bool success = false, free_entries = false;
bool success = false;
/* Allocate memory for the ncm application content meta keys. */ /* Allocate memory for the ncm application content meta keys. */
meta_keys = calloc(1, meta_keys_size); meta_keys = calloc(1, meta_keys_size);
@ -1360,30 +1498,55 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
} }
} }
/* Reallocate title info buffer. */ /* Reallocate title info pointer array. */
/* If g_titleInfo == NULL, realloc() will essentially act as a malloc(). */ if (!titleReallocateTitleInfo(total, false))
tmp_title_info = realloc(g_titleInfo, (g_titleInfoCount + total) * sizeof(TitleInfo));
if (!tmp_title_info)
{ {
LOG_MSG("Unable to reallocate title info buffer! (%u %s).", g_titleInfoCount + total, (g_titleInfoCount + total) > 1 ? "entries" : "entry"); LOG_MSG("Failed to reallocate title info pointer array for storage ID %u!", storage_id);
goto end; goto end;
} }
g_titleInfo = tmp_title_info; free_entries = true;
tmp_title_info = NULL;
/* Clear new title info buffer area. */
memset(g_titleInfo + g_titleInfoCount, 0, total * sizeof(TitleInfo));
/* Fill new title info entries. */ /* Fill new title info entries. */
for(u32 i = 0; i < total; i++) for(u32 i = 0; i < total; i++)
{ {
TitleInfo *cur_title_info = &(g_titleInfo[g_titleInfoCount + i]); u64 tmp_size = 0;
NcmContentMetaKey *cur_meta_key = &(meta_keys[i]);
TitleInfo *cur_title_info = g_titleInfo[g_titleInfoCount + extra_title_count];
if (!cur_title_info)
{
/* Allocate memory for a new entry. */
cur_title_info = calloc(1, sizeof(TitleInfo));
if (!cur_title_info)
{
LOG_MSG("Failed to allocate memory for title info entry #%u! (%u / %u).", extra_title_count, i + 1, total);
goto end;
}
/* Set title info entry pointer. */
g_titleInfo[g_titleInfoCount + extra_title_count] = cur_title_info;
}
/* Get content infos. */
if (!titleGetContentInfosFromTitle(storage_id, cur_meta_key, &(cur_title_info->content_infos), &(cur_title_info->content_count)))
{
LOG_MSG("Failed to get content infos for title ID %016lX!", cur_meta_key->id);
continue;
}
/* Calculate title size. */
for(u32 j = 0; j < cur_title_info->content_count; j++)
{
titleConvertNcmContentSizeToU64(cur_title_info->content_infos[j].size, &tmp_size);
cur_title_info->size += tmp_size;
}
/* Fill information. */ /* Fill information. */
cur_title_info->storage_id = storage_id; cur_title_info->storage_id = storage_id;
memcpy(&(cur_title_info->meta_key), &(meta_keys[i]), sizeof(NcmContentMetaKey)); memcpy(&(cur_title_info->meta_key), cur_meta_key, sizeof(NcmContentMetaKey));
cur_title_info->version.value = cur_title_info->meta_key.version; cur_title_info->version.value = cur_title_info->meta_key.version;
utilsGenerateFormattedSizeString(cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
/* Retrieve application metadata. */ /* Retrieve application metadata. */
u64 app_id = (cur_title_info->meta_key.type <= NcmContentMetaType_Application ? cur_title_info->meta_key.id : \ u64 app_id = (cur_title_info->meta_key.type <= NcmContentMetaType_Application ? cur_title_info->meta_key.id : \
@ -1393,24 +1556,22 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id); cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id);
/* Retrieve content infos. */ /* Increase extra title info counter. */
if (titleGetContentInfosFromTitle(storage_id, &(cur_title_info->meta_key), &(cur_title_info->content_infos), &(cur_title_info->content_count))) extra_title_count++;
{ }
/* Calculate title size. */
u64 tmp_size = 0; /* Check retrieved title info count. */
for(u32 j = 0; j < cur_title_info->content_count; j++) if (!extra_title_count)
{ {
titleConvertNcmContentSizeToU64(cur_title_info->content_infos[j].size, &tmp_size); LOG_MSG("Unable to generate title info entries! (%u element[s]).", total);
cur_title_info->size += tmp_size; goto end;
}
}
/* Generate formatted title size string. */
utilsGenerateFormattedSizeString(cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
} }
/* Update title info count. */ /* Update title info count. */
g_titleInfoCount += total; g_titleInfoCount += extra_title_count;
/* Free extra allocated pointers if we didn't use them. */
if (extra_title_count < total) titleReallocateTitleInfo(0, false);
/* Update linked lists for user applications, patches and add-on contents. */ /* Update linked lists for user applications, patches and add-on contents. */
/* This will also keep track of orphan titles - titles with no available application metadata. */ /* This will also keep track of orphan titles - titles with no available application metadata. */
@ -1422,6 +1583,9 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
end: end:
if (meta_keys) free(meta_keys); if (meta_keys) free(meta_keys);
/* Free previously allocated title info pointers. Ignore return value. */
if (!success && free_entries) titleReallocateTitleInfo(extra_title_count, true);
return success; return success;
} }
@ -1510,14 +1674,16 @@ static void titleUpdateTitleInfoLinkedLists(void)
for(u32 i = 0; i < g_titleInfoCount; i++) for(u32 i = 0; i < g_titleInfoCount; i++)
{ {
/* Get pointer to the current title info and reset its linked list pointers. */ /* Get pointer to the current title info and reset its linked list pointers. */
TitleInfo *child_info = &(g_titleInfo[i]); TitleInfo *child_info = g_titleInfo[i];
if (!child_info) continue;
child_info->parent = child_info->previous = child_info->next = NULL; child_info->parent = child_info->previous = child_info->next = NULL;
if (child_info->meta_key.type < NcmContentMetaType_Application) if (child_info->meta_key.type < NcmContentMetaType_Application)
{ {
/* We're dealing with a system title. */ /* We're dealing with a system title. */
/* Increase orphan title count if we have no application metadata. Immediately proceed onto the next loop iteration. */ /* Increase orphan title count if we have no application metadata. Immediately proceed onto the next loop iteration. */
/* We don't generate linked lists for orphan titles nor system titles. */ /* We don't generate linked lists for system titles. */
if (!child_info->app_metadata) g_titleInfoOrphanCount++; if (!child_info->app_metadata) g_titleInfoOrphanCount++;
continue; continue;
} else } else
@ -1528,7 +1694,8 @@ static void titleUpdateTitleInfoLinkedLists(void)
/* Since gamecard title info entries are always appended to the end of the buffer, this guarantees we will first retrieve an eMMC / SD card entry (if available). */ /* Since gamecard title info entries are always appended to the end of the buffer, this guarantees we will first retrieve an eMMC / SD card entry (if available). */
for(u32 j = 0; j < g_titleInfoCount; j++) for(u32 j = 0; j < g_titleInfoCount; j++)
{ {
TitleInfo *parent_info = &(g_titleInfo[j]); TitleInfo *parent_info = g_titleInfo[j];
if (!parent_info) continue;
if (parent_info->meta_key.type == NcmContentMetaType_Application && \ if (parent_info->meta_key.type == NcmContentMetaType_Application && \
((child_info->meta_key.type == NcmContentMetaType_Patch && titleCheckIfPatchIdBelongsToApplicationId(parent_info->meta_key.id, child_info->meta_key.id)) || \ ((child_info->meta_key.type == NcmContentMetaType_Patch && titleCheckIfPatchIdBelongsToApplicationId(parent_info->meta_key.id, child_info->meta_key.id)) || \
@ -1540,20 +1707,16 @@ static void titleUpdateTitleInfoLinkedLists(void)
} }
} }
/* If we have no application metadata, increase orphan title count and proceed onto the next loop iteration. */ /* If we have no application metadata, increase orphan title count. */
/* We don't generate linked lists for orphan titles. */ if (!child_info->app_metadata) g_titleInfoOrphanCount++;
if (!child_info->app_metadata)
{
g_titleInfoOrphanCount++;
continue;
}
} }
/* Locate previous user application, patch or add-on content entry. */ /* Locate previous user application, patch or add-on content entry. */
/* If it's found, we will update both its next pointer and the previous pointer from the current entry. */ /* If it's found, we will update both its next pointer and the previous pointer from the current entry. */
for(u32 j = i; j > 0; j--) for(u32 j = i; j > 0; j--)
{ {
TitleInfo *previous_info = &(g_titleInfo[j - 1]); TitleInfo *previous_info = g_titleInfo[j - 1];
if (!previous_info) continue;
if (previous_info->meta_key.type == child_info->meta_key.type && (((child_info->meta_key.type == NcmContentMetaType_Application || child_info->meta_key.type == NcmContentMetaType_Patch) && \ if (previous_info->meta_key.type == child_info->meta_key.type && (((child_info->meta_key.type == NcmContentMetaType_Application || child_info->meta_key.type == NcmContentMetaType_Patch) && \
previous_info->meta_key.id == child_info->meta_key.id) || (child_info->meta_key.type == NcmContentMetaType_AddOnContent && \ previous_info->meta_key.id == child_info->meta_key.id) || (child_info->meta_key.type == NcmContentMetaType_AddOnContent && \
@ -1652,9 +1815,8 @@ static void titleGameCardInfoThreadFunc(void *arg)
static bool titleRefreshGameCardTitleInfo(void) static bool titleRefreshGameCardTitleInfo(void)
{ {
TitleApplicationMetadata *tmp_app_metadata = NULL; u32 gamecard_app_count = 0, extra_app_count = 0;
u32 orig_app_count = g_appMetadataCount, cur_app_count = g_appMetadataCount, gamecard_metadata_count = 0; bool status = false, success = false, cleanup = true, free_entries = false;
bool status = false, success = false, cleanup = true;
/* Retrieve current gamecard status. */ /* Retrieve current gamecard status. */
status = (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded); status = (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded);
@ -1689,10 +1851,35 @@ static bool titleRefreshGameCardTitleInfo(void)
goto end; goto end;
} }
/* Retrieve gamecard application metadata. */ /* Get gamecard user application count. */
for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++) for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++)
{ {
TitleInfo *cur_title_info = &(g_titleInfo[i]); TitleInfo *cur_title_info = g_titleInfo[i];
if (cur_title_info && cur_title_info->meta_key.type == NcmContentMetaType_Application) gamecard_app_count++;
}
/* Return immediately if, for some reason, there are no user applications. */
if (!gamecard_app_count)
{
success = true;
cleanup = false;
goto end;
}
/* Reallocate application metadata pointer array. */
if (!titleReallocateApplicationMetadata(gamecard_app_count, false))
{
LOG_MSG("Failed to reallocate application metadata pointer array for gamecard user applications!");
goto end;
}
free_entries = true;
/* Retrieve application metadata for gamecard user applications. */
for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++)
{
TitleInfo *cur_title_info = g_titleInfo[i];
if (!cur_title_info) continue;
u64 app_id = (cur_title_info->meta_key.type <= NcmContentMetaType_Application ? cur_title_info->meta_key.id : \ u64 app_id = (cur_title_info->meta_key.type <= NcmContentMetaType_Application ? cur_title_info->meta_key.id : \
(cur_title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(cur_title_info->meta_key.id) : \ (cur_title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(cur_title_info->meta_key.id) : \
@ -1702,34 +1889,40 @@ static bool titleRefreshGameCardTitleInfo(void)
/* Do not proceed if application metadata has already been retrieved, or if we can successfully retrieve it. */ /* Do not proceed if application metadata has already been retrieved, or if we can successfully retrieve it. */
if (cur_title_info->app_metadata != NULL || (cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id)) != NULL) continue; if (cur_title_info->app_metadata != NULL || (cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id)) != NULL) continue;
/* Reallocate application metadata buffer (if needed). */ /* Retrieve application metadata pointer. */
if (cur_app_count < (g_appMetadataCount + 1)) TitleApplicationMetadata *cur_app_metadata = g_appMetadata[g_appMetadataCount + extra_app_count];
if (!cur_app_metadata)
{ {
tmp_app_metadata = realloc(g_appMetadata, (g_appMetadataCount + 1) * sizeof(TitleApplicationMetadata)); /* Allocate memory for a new application metadata entry. */
if (!tmp_app_metadata) cur_app_metadata = calloc(1, sizeof(TitleApplicationMetadata));
if (!cur_app_metadata)
{ {
LOG_MSG("Failed to reallocate application metadata buffer! (additional entry)."); LOG_MSG("Failed to allocate memory for application metadata entry #%u!", extra_app_count);
goto end; goto end;
} }
g_appMetadata = tmp_app_metadata; /* Set application metadata entry pointer. */
tmp_app_metadata = NULL; g_appMetadata[g_appMetadataCount + extra_app_count] = cur_app_metadata;
cur_app_count++;
} }
/* Retrieve application metadata. */ /* Retrieve application metadata. */
if (!titleRetrieveApplicationMetadataByTitleId(app_id, &(g_appMetadata[g_appMetadataCount]))) continue; if (!titleRetrieveApplicationMetadataByTitleId(app_id, cur_app_metadata)) continue;
cur_title_info->app_metadata = &(g_appMetadata[g_appMetadataCount++]); /* Update application metadata pointer in title info. */
gamecard_metadata_count++; cur_title_info->app_metadata = cur_app_metadata;
/* Increase extra application metadata counter. */
extra_app_count++;
} }
/* Check if we retrieved new application metadata that was previously unavailable. */ if (extra_app_count)
if (gamecard_metadata_count)
{ {
/* Update application metadata count. */
g_appMetadataCount += extra_app_count;
/* Sort application metadata entries by name. */ /* Sort application metadata entries by name. */
qsort(g_appMetadata + g_systemTitlesCount, gamecard_metadata_count, sizeof(TitleApplicationMetadata), &titleUserApplicationMetadataEntrySortFunction); u32 user_app_metadata_count = (g_appMetadataCount - g_systemTitlesCount);
if (user_app_metadata_count > 1) qsort(g_appMetadata + g_systemTitlesCount, user_app_metadata_count, sizeof(TitleApplicationMetadata*), &titleUserApplicationMetadataEntrySortFunction);
/* Check if the orphan title count is non-zero. */ /* Check if the orphan title count is non-zero. */
if (g_titleInfoOrphanCount) if (g_titleInfoOrphanCount)
@ -1740,8 +1933,8 @@ static bool titleRefreshGameCardTitleInfo(void)
/* Try to update the application metadata pointer in orphan entries, hopefully reducing the orphan title count in the process. */ /* Try to update the application metadata pointer in orphan entries, hopefully reducing the orphan title count in the process. */
for(u32 i = 0; i < g_titleInfoCount; i++) for(u32 i = 0; i < g_titleInfoCount; i++)
{ {
TitleInfo *title_info = &(g_titleInfo[i]); TitleInfo *title_info = g_titleInfo[i];
if ((title_info->meta_key.type != NcmContentMetaType_Patch && title_info->meta_key.type != NcmContentMetaType_AddOnContent) || title_info->app_metadata) continue; if (!title_info || (title_info->meta_key.type != NcmContentMetaType_Patch && title_info->meta_key.type != NcmContentMetaType_AddOnContent) || title_info->app_metadata) continue;
u64 app_id = (title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(title_info->meta_key.id) : \ u64 app_id = (title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetApplicationIdByPatchId(title_info->meta_key.id) : \
titleGetApplicationIdByAddOnContentId(title_info->meta_key.id)); titleGetApplicationIdByAddOnContentId(title_info->meta_key.id));
@ -1749,8 +1942,15 @@ static bool titleRefreshGameCardTitleInfo(void)
if (!(title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id))) g_titleInfoOrphanCount++; if (!(title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id))) g_titleInfoOrphanCount++;
} }
} }
} else {
/* Free leftover application metadata entry (if needed). */
if (g_appMetadata[g_appMetadataCount]) free(g_appMetadata[g_appMetadataCount]);
} }
/* Free extra allocated pointers if we didn't use them. */
if (extra_app_count < gamecard_app_count) titleReallocateApplicationMetadata(0, false);
/* Update flags. */
success = true; success = true;
cleanup = false; cleanup = false;
@ -1758,18 +1958,8 @@ end:
/* Update gamecard status. */ /* Update gamecard status. */
g_titleGameCardAvailable = status; g_titleGameCardAvailable = status;
/* Decrease application metadata buffer size (if needed). */ /* Free previously allocated application metadata pointers. Ignore return value. */
if ((success && g_appMetadataCount < cur_app_count) || (!success && g_appMetadataCount > orig_app_count)) if (!success && free_entries) titleReallocateApplicationMetadata(extra_app_count, true);
{
if (!success) g_appMetadataCount = orig_app_count;
tmp_app_metadata = realloc(g_appMetadata, g_appMetadataCount * sizeof(TitleApplicationMetadata));
if (tmp_app_metadata)
{
g_appMetadata = tmp_app_metadata;
tmp_app_metadata = NULL;
}
}
/* Remove gamecard title info entries and close its ncm database and storage handles (if needed). */ /* Remove gamecard title info entries and close its ncm database and storage handles (if needed). */
if (cleanup) if (cleanup)
@ -1783,45 +1973,31 @@ end:
static void titleRemoveGameCardTitleInfoEntries(void) static void titleRemoveGameCardTitleInfoEntries(void)
{ {
if (!g_titleInfo || !g_titleInfoCount || !g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || \ if (!g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || \
g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount)) return; g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount)) return;
if (g_titleInfoGameCardCount == g_titleInfoCount) /* Update title info count. */
{ g_titleInfoCount = g_titleInfoGameCardStartIndex;
/* Free all title info entries. */
titleFreeTitleInfo(); /* Reallocate title info pointer array. */
} else { titleReallocateTitleInfo(g_titleInfoGameCardCount - 1, true);
/* Free content infos from gamecard title info entries. */
for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++) /* Update gamecard variables. */
{ g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
TitleInfo *cur_title_info = &(g_titleInfo[i]);
if (cur_title_info->content_infos) free(cur_title_info->content_infos); /* Update linked lists for user applications, patches and add-on contents. */
} /* Won't do anything if g_titleInfoCount is already zero. */
titleUpdateTitleInfoLinkedLists();
/* Reallocate title info buffer. */
TitleInfo *tmp_title_info = realloc(g_titleInfo, g_titleInfoGameCardStartIndex * sizeof(TitleInfo));
if (tmp_title_info)
{
g_titleInfo = tmp_title_info;
tmp_title_info = NULL;
}
/* Update counters. */
g_titleInfoCount = g_titleInfoGameCardStartIndex;
g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
/* Update linked lists for user applications, patches and add-on contents. */
titleUpdateTitleInfoLinkedLists();
}
} }
static bool titleIsUserApplicationContentAvailable(u64 app_id) static bool titleIsUserApplicationContentAvailable(u64 app_id)
{ {
if (!g_titleInfo || !g_titleInfoCount || !app_id) return false; if (!g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !app_id) return false;
for(u32 i = 0; i < g_titleInfoCount; i++) for(u32 i = 0; i < g_titleInfoCount; i++)
{ {
TitleInfo *cur_title_info = &(g_titleInfo[i]); TitleInfo *cur_title_info = g_titleInfo[i];
if (!cur_title_info) continue;
if ((cur_title_info->meta_key.type == NcmContentMetaType_Application && cur_title_info->meta_key.id == app_id) || \ if ((cur_title_info->meta_key.type == NcmContentMetaType_Application && cur_title_info->meta_key.id == app_id) || \
(cur_title_info->meta_key.type == NcmContentMetaType_Patch && titleCheckIfPatchIdBelongsToApplicationId(app_id, cur_title_info->meta_key.id)) || \ (cur_title_info->meta_key.type == NcmContentMetaType_Patch && titleCheckIfPatchIdBelongsToApplicationId(app_id, cur_title_info->meta_key.id)) || \
@ -1837,7 +2013,7 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id,
TitleInfo *info = NULL; TitleInfo *info = NULL;
if (!g_titleInterfaceInit || !g_titleInfo || !g_titleInfoCount || storage_id < NcmStorageId_GameCard || storage_id > NcmStorageId_Any || (storage_id == NcmStorageId_GameCard && \ if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || storage_id < NcmStorageId_GameCard || storage_id > NcmStorageId_Any || (storage_id == NcmStorageId_GameCard && \
(!g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount))) || !title_id) (!g_titleInfoGameCardCount || g_titleInfoGameCardCount > g_titleInfoCount || g_titleInfoGameCardStartIndex != (g_titleInfoCount - g_titleInfoGameCardCount))) || !title_id)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
@ -1850,9 +2026,9 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id,
for(u32 i = start_idx; i < max_val; i++) for(u32 i = start_idx; i < max_val; i++)
{ {
TitleInfo *title_info = &(g_titleInfo[i]); TitleInfo *title_info = g_titleInfo[i];
if (title_info->meta_key.id == title_id && (storage_id == NcmStorageId_Any || (storage_id != NcmStorageId_Any && title_info->storage_id == storage_id))) if (title_info && title_info->meta_key.id == title_id && (storage_id == NcmStorageId_Any || (storage_id != NcmStorageId_Any && title_info->storage_id == storage_id)))
{ {
info = title_info; info = title_info;
break; break;
@ -1869,8 +2045,8 @@ end:
static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b) static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b)
{ {
const TitleApplicationMetadata *app_metadata_1 = (const TitleApplicationMetadata*)a; const TitleApplicationMetadata *app_metadata_1 = *((const TitleApplicationMetadata**)a);
const TitleApplicationMetadata *app_metadata_2 = (const TitleApplicationMetadata*)b; const TitleApplicationMetadata *app_metadata_2 = *((const TitleApplicationMetadata**)b);
if (app_metadata_1->title_id < app_metadata_2->title_id) if (app_metadata_1->title_id < app_metadata_2->title_id)
{ {
@ -1886,8 +2062,8 @@ static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *
static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b) static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b)
{ {
const TitleApplicationMetadata *app_metadata_1 = (const TitleApplicationMetadata*)a; const TitleApplicationMetadata *app_metadata_1 = *((const TitleApplicationMetadata**)a);
const TitleApplicationMetadata *app_metadata_2 = (const TitleApplicationMetadata*)b; const TitleApplicationMetadata *app_metadata_2 = *((const TitleApplicationMetadata**)b);
return strcasecmp(app_metadata_1->lang_entry.name, app_metadata_2->lang_entry.name); return strcasecmp(app_metadata_1->lang_entry.name, app_metadata_2->lang_entry.name);
} }

View file

@ -139,7 +139,9 @@ NX_INLINE void titleConvertNcmContentSizeToU64(const u8 *size, u64 *out)
NX_INLINE void titleConvertU64ToNcmContentSize(const u64 *size, u8 *out) NX_INLINE void titleConvertU64ToNcmContentSize(const u64 *size, u8 *out)
{ {
if (size && out) memcpy(out, size, 6); if (!size || !out) return;
memcpy(out, size, 6);
out[6] = out[7] = 0;
} }
NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id) NX_INLINE u64 titleGetPatchIdByApplicationId(u64 app_id)