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:
parent
8be5460229
commit
a01b7846de
7 changed files with 438 additions and 235 deletions
|
@ -349,7 +349,7 @@ static void waitForGameCardAndUsb(void)
|
|||
|
||||
while(true)
|
||||
{
|
||||
if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && !titleIsGameCardInfoUpdated()) break;
|
||||
if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && titleIsGameCardInfoUpdated()) break;
|
||||
}
|
||||
|
||||
consolePrint("waiting for usb session...\n");
|
||||
|
|
|
@ -356,55 +356,57 @@ bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out)
|
|||
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;
|
||||
|
||||
mutexLock(&g_gameCardMutex);
|
||||
|
||||
/* Free Hash FS context. */
|
||||
hfsFreeContext(out);
|
||||
|
||||
/* Get pointer to the Hash FS context for the requested partition. */
|
||||
fs_ctx = _gamecardGetHashFileSystemContext(hfs_partition_type);
|
||||
if (!fs_ctx) goto end;
|
||||
|
||||
/* Create a copy of the retrieved Hash FS context. */
|
||||
/* We won't return a pointer to any of our internal Hash FS contexts (for concurrency reasons). */
|
||||
fs_ctx_copy = calloc(1, sizeof(HashFileSystemContext));
|
||||
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)
|
||||
/* Fill Hash FS context. */
|
||||
out->name = strdup(fs_ctx->name);
|
||||
if (!out->name)
|
||||
{
|
||||
LOG_MSG("Failed to duplicate Hash FS partition name! (%s).", fs_ctx->name);
|
||||
goto end;
|
||||
}
|
||||
|
||||
fs_ctx_copy->offset = fs_ctx->offset;
|
||||
fs_ctx_copy->size = fs_ctx->size;
|
||||
fs_ctx_copy->header_size = fs_ctx->header_size;
|
||||
out->type = fs_ctx->type;
|
||||
out->offset = fs_ctx->offset;
|
||||
out->size = fs_ctx->size;
|
||||
out->header_size = fs_ctx->header_size;
|
||||
|
||||
fs_ctx_copy->header = calloc(fs_ctx->header_size, sizeof(u8));
|
||||
if (!fs_ctx_copy->header)
|
||||
out->header = calloc(fs_ctx->header_size, sizeof(u8));
|
||||
if (!out->header)
|
||||
{
|
||||
LOG_MSG("Failed to duplicate Hash FS partition header! (%s).", fs_ctx->name);
|
||||
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. */
|
||||
success = true;
|
||||
|
||||
end:
|
||||
if (!success) hfsFreeContext(&fs_ctx_copy);
|
||||
if (!success) hfsFreeContext(out);
|
||||
|
||||
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)
|
||||
|
@ -622,7 +624,11 @@ end:
|
|||
{
|
||||
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();
|
||||
}
|
||||
|
@ -638,7 +644,15 @@ static void gamecardFreeInfo(void)
|
|||
|
||||
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);
|
||||
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)
|
||||
{
|
||||
u32 magic = 0;
|
||||
u32 i = 0, magic = 0;
|
||||
HashFileSystemContext *fs_ctx = NULL;
|
||||
HashFileSystemHeader fs_header = {0};
|
||||
u8 fs_header_hash[SHA256_HASH_SIZE] = {0};
|
||||
|
@ -982,6 +996,20 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char
|
|||
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. */
|
||||
if (!gamecardReadStorageArea(&fs_header, sizeof(HashFileSystemHeader), offset, false))
|
||||
{
|
||||
|
|
|
@ -225,10 +225,9 @@ bool gamecardGetRomCapacity(u64 *out);
|
|||
/// Fills the provided VersionType1 pointer with the bundled firmware update version in the inserted gamecard.
|
||||
bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out);
|
||||
|
||||
/// Returns a pointer to a dynamically-allocated HashFileSystemContext for the provided Hash FS partition type.
|
||||
/// Hash FS functions can be used on the retrieved HashFileSystemContext. hfsFreeContext() must be used to free the returned context.
|
||||
/// Returns NULL if an error occurs.
|
||||
HashFileSystemContext *gamecardGetHashFileSystemContext(u8 hfs_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 underlying data from the filled context.
|
||||
bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out);
|
||||
|
||||
/// 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.
|
||||
|
|
14
source/hfs.h
14
source/hfs.h
|
@ -44,6 +44,7 @@ typedef struct {
|
|||
/// Internally used by gamecard functions.
|
||||
/// Use gamecardGetHashFileSystemContext() to retrieve a Hash FS context.
|
||||
typedef struct {
|
||||
u8 type; ///< GameCardHashFileSystemPartitionType.
|
||||
char *name; ///< Dynamically allocated partition name.
|
||||
u64 offset; ///< Partition offset (relative to the start of gamecard image).
|
||||
u64 size; ///< Partition size.
|
||||
|
@ -67,15 +68,12 @@ bool hfsGetTotalDataSize(HashFileSystemContext *ctx, u64 *out_size);
|
|||
|
||||
/// Miscellaneous functions.
|
||||
|
||||
NX_INLINE void hfsFreeContext(HashFileSystemContext **ctx)
|
||||
NX_INLINE void hfsFreeContext(HashFileSystemContext *ctx)
|
||||
{
|
||||
if (!ctx || !*ctx) return;
|
||||
|
||||
if ((*ctx)->name) free((*ctx)->name);
|
||||
if ((*ctx)->header) free((*ctx)->header);
|
||||
|
||||
free(*ctx);
|
||||
*ctx = NULL;
|
||||
if (!ctx) return;
|
||||
if (ctx->name) free(ctx->name);
|
||||
if (ctx->header) free(ctx->header);
|
||||
memset(ctx, 0, sizeof(HashFileSystemContext));
|
||||
}
|
||||
|
||||
NX_INLINE u32 hfsGetEntryCount(HashFileSystemContext *ctx)
|
||||
|
|
|
@ -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) && \
|
||||
(((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);
|
||||
if (!tmp)
|
||||
{
|
||||
|
|
568
source/title.c
568
source/title.c
|
@ -42,13 +42,13 @@ static bool g_titleInterfaceInit = false, g_titleGameCardInfoThreadCreated = fal
|
|||
|
||||
static NsApplicationControlData *g_nsAppControlData = NULL;
|
||||
|
||||
static TitleApplicationMetadata *g_appMetadata = 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 TitleInfo **g_titleInfo = NULL;
|
||||
static u32 g_titleInfoCount = 0, g_titleInfoGameCardStartIndex = 0, g_titleInfoGameCardCount = 0, g_titleInfoOrphanCount = 0;
|
||||
|
||||
static const char *g_titleNcmContentTypeNames[] = {
|
||||
|
@ -380,7 +380,10 @@ static const u32 g_systemTitlesCount = MAX_ELEMENTS(g_systemTitles);
|
|||
/* Function prototypes. */
|
||||
|
||||
NX_INLINE void titleFreeApplicationMetadata(void);
|
||||
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool free_entries);
|
||||
|
||||
NX_INLINE void titleFreeTitleInfo(void);
|
||||
static bool titleReallocateTitleInfo(u32 extra_title_count, bool free_entries);
|
||||
|
||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id);
|
||||
|
||||
|
@ -588,7 +591,7 @@ TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u3
|
|||
u32 app_count = 0;
|
||||
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!");
|
||||
goto end;
|
||||
|
@ -596,17 +599,17 @@ TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u3
|
|||
|
||||
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. */
|
||||
if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id, false)) || \
|
||||
(!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*));
|
||||
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);
|
||||
app_metadata = NULL;
|
||||
goto end;
|
||||
|
@ -641,7 +644,7 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out)
|
|||
|
||||
bool success = false;
|
||||
|
||||
if (!g_titleInterfaceInit || !out)
|
||||
if (!g_titleInterfaceInit || !g_titleInfo || !*g_titleInfo || !g_titleInfoCount || !app_id || !out)
|
||||
{
|
||||
LOG_MSG("Invalid parameters!");
|
||||
goto end;
|
||||
|
@ -659,8 +662,8 @@ bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out)
|
|||
/* Get first add-on content title info. */
|
||||
for(u32 i = 0; i < g_titleInfoCount; i++)
|
||||
{
|
||||
TitleInfo *title_info = &(g_titleInfo[i]);
|
||||
if (title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id))
|
||||
TitleInfo *title_info = g_titleInfo[i];
|
||||
if (title_info && title_info->meta_key.type == NcmContentMetaType_AddOnContent && titleCheckIfAddOnContentIdBelongsToApplicationId(app_id, title_info->meta_key.id))
|
||||
{
|
||||
out->aoc_info = title_info;
|
||||
break;
|
||||
|
@ -695,25 +698,25 @@ TitleInfo **titleGetInfoFromOrphanTitles(u32 *out_count)
|
|||
|
||||
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!");
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* Allocate orphan title info buffer. */
|
||||
/* Allocate orphan title info pointer array. */
|
||||
orphan_info = calloc(g_titleInfoOrphanCount, sizeof(TitleInfo*));
|
||||
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;
|
||||
}
|
||||
|
||||
/* Get pointers to orphan title info entries. */
|
||||
for(u32 i = 0, j = 0; i < g_titleInfoCount && j < g_titleInfoOrphanCount; i++)
|
||||
{
|
||||
TitleInfo *title_info = &(g_titleInfo[i]);
|
||||
if (title_info->meta_key.type != NcmContentMetaType_Application && !title_info->app_metadata) orphan_info[j++] = title_info;
|
||||
TitleInfo *title_info = g_titleInfo[i];
|
||||
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. */
|
||||
|
@ -801,7 +804,7 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
|
|||
char *filename = NULL, *tmp_filename = NULL;
|
||||
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 || \
|
||||
(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++)
|
||||
{
|
||||
TitleInfo *app_info = &(g_titleInfo[i]);
|
||||
u32 app_version = app_info->meta_key.version;
|
||||
TitleInfo *app_info = g_titleInfo[i];
|
||||
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. */
|
||||
/* 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;
|
||||
|
||||
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) || \
|
||||
TitleInfo *patch_info = g_titleInfo[j];
|
||||
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;
|
||||
|
||||
app_version = patch_info->meta_key.version;
|
||||
|
@ -900,7 +903,12 @@ NX_INLINE void titleFreeApplicationMetadata(void)
|
|||
{
|
||||
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);
|
||||
|
@ -910,13 +918,74 @@ NX_INLINE void titleFreeApplicationMetadata(void)
|
|||
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)
|
||||
{
|
||||
if (g_titleInfo)
|
||||
{
|
||||
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);
|
||||
|
@ -926,13 +995,70 @@ NX_INLINE void titleFreeTitleInfo(void)
|
|||
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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
|
@ -940,40 +1066,50 @@ NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 ti
|
|||
|
||||
static bool titleGenerateMetadataEntriesFromSystemTitles(void)
|
||||
{
|
||||
TitleApplicationMetadata *tmp_app_metadata = NULL;
|
||||
u32 extra_app_count = 0;
|
||||
bool success = false;
|
||||
|
||||
/* Reallocate application metadata buffer. */
|
||||
/* If g_appMetadata == NULL, realloc() will essentially act as a malloc(). */
|
||||
tmp_app_metadata = realloc(g_appMetadata, (g_appMetadataCount + g_systemTitlesCount) * sizeof(TitleApplicationMetadata));
|
||||
if (!tmp_app_metadata)
|
||||
/* Reallocate application metadata pointer array. */
|
||||
if (!titleReallocateApplicationMetadata(g_systemTitlesCount, false))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
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. */
|
||||
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]);
|
||||
const SystemTitleName *system_title = &(g_systemTitles[i]);
|
||||
/* Allocate memory for the current entry. */
|
||||
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;
|
||||
sprintf(app_metadata->lang_entry.name, system_title->name);
|
||||
/* Fill information. */
|
||||
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. */
|
||||
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. */
|
||||
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)
|
||||
|
@ -981,22 +1117,19 @@ static bool titleGenerateMetadataEntriesFromNsRecords(void)
|
|||
Result rc = 0;
|
||||
|
||||
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;
|
||||
TitleApplicationMetadata *tmp_app_metadata = NULL;
|
||||
bool success = false, free_entries = false;
|
||||
|
||||
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));
|
||||
if (!app_records)
|
||||
{
|
||||
LOG_MSG("Failed to allocate memory for ns application records!");
|
||||
goto end;
|
||||
LOG_MSG("Failed to allocate memory for NS application records!");
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Retrieve ns application records. */
|
||||
/* Retrieve NS application records. */
|
||||
rc = nsListApplicationRecord(app_records, NS_APPLICATION_RECORD_LIMIT, 0, (s32*)&app_records_count);
|
||||
if (R_FAILED(rc))
|
||||
{
|
||||
|
@ -1011,57 +1144,64 @@ static bool titleGenerateMetadataEntriesFromNsRecords(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Reallocate application metadata buffer. */
|
||||
tmp_app_metadata = realloc(g_appMetadata, (g_appMetadataCount + app_records_count) * sizeof(TitleApplicationMetadata));
|
||||
if (!tmp_app_metadata)
|
||||
/* Reallocate application metadata pointer array. */
|
||||
if (!titleReallocateApplicationMetadata(app_records_count, false))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
g_appMetadata = tmp_app_metadata;
|
||||
tmp_app_metadata = NULL;
|
||||
free_entries = true;
|
||||
|
||||
/* 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++)
|
||||
{
|
||||
if (!titleRetrieveApplicationMetadataByTitleId(app_records[i].application_id, &(g_appMetadata[g_appMetadataCount + new_app_count]))) continue;
|
||||
new_app_count++;
|
||||
TitleApplicationMetadata *cur_app_metadata = g_appMetadata[g_appMetadataCount + extra_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. */
|
||||
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;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
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;
|
||||
|
||||
end:
|
||||
if (app_records)
|
||||
{
|
||||
/* Decrease application metadata buffer size if needed. */
|
||||
if (app_records_count && g_appMetadataCount < (cur_app_count + app_records_count))
|
||||
{
|
||||
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);
|
||||
}
|
||||
if (app_records) free(app_records);
|
||||
|
||||
/* Free previously allocated application metadata pointers. Ignore return value. */
|
||||
if (!success && free_entries) titleReallocateApplicationMetadata(extra_app_count, true);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
@ -1267,7 +1407,7 @@ static void titleCloseNcmDatabaseAndStorageFromGameCard(void)
|
|||
static bool titleLoadPersistentStorageTitleInfo(void)
|
||||
{
|
||||
/* 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++)
|
||||
{
|
||||
|
@ -1294,13 +1434,11 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
|
|||
|
||||
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;
|
||||
size_t meta_keys_size = sizeof(NcmContentMetaKey);
|
||||
|
||||
TitleInfo *tmp_title_info = NULL;
|
||||
|
||||
bool success = false;
|
||||
bool success = false, free_entries = false;
|
||||
|
||||
/* Allocate memory for the ncm application content meta keys. */
|
||||
meta_keys = calloc(1, meta_keys_size);
|
||||
|
@ -1360,30 +1498,55 @@ static bool titleGenerateTitleInfoFromStorage(u8 storage_id)
|
|||
}
|
||||
}
|
||||
|
||||
/* 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)
|
||||
/* Reallocate title info pointer array. */
|
||||
if (!titleReallocateTitleInfo(total, false))
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
g_titleInfo = tmp_title_info;
|
||||
tmp_title_info = NULL;
|
||||
|
||||
/* Clear new title info buffer area. */
|
||||
memset(g_titleInfo + g_titleInfoCount, 0, total * sizeof(TitleInfo));
|
||||
free_entries = true;
|
||||
|
||||
/* Fill new title info entries. */
|
||||
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. */
|
||||
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;
|
||||
utilsGenerateFormattedSizeString(cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
|
||||
|
||||
/* Retrieve application metadata. */
|
||||
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);
|
||||
|
||||
/* Retrieve content infos. */
|
||||
if (titleGetContentInfosFromTitle(storage_id, &(cur_title_info->meta_key), &(cur_title_info->content_infos), &(cur_title_info->content_count)))
|
||||
{
|
||||
/* Calculate title size. */
|
||||
u64 tmp_size = 0;
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
/* Generate formatted title size string. */
|
||||
utilsGenerateFormattedSizeString(cur_title_info->size, cur_title_info->size_str, sizeof(cur_title_info->size_str));
|
||||
/* Increase extra title info counter. */
|
||||
extra_title_count++;
|
||||
}
|
||||
|
||||
/* Check retrieved title info count. */
|
||||
if (!extra_title_count)
|
||||
{
|
||||
LOG_MSG("Unable to generate title info entries! (%u element[s]).", total);
|
||||
goto end;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
/* 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:
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -1510,14 +1674,16 @@ static void titleUpdateTitleInfoLinkedLists(void)
|
|||
for(u32 i = 0; i < g_titleInfoCount; i++)
|
||||
{
|
||||
/* 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;
|
||||
|
||||
if (child_info->meta_key.type < NcmContentMetaType_Application)
|
||||
{
|
||||
/* We're dealing with a system title. */
|
||||
/* 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++;
|
||||
continue;
|
||||
} 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). */
|
||||
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 && \
|
||||
((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. */
|
||||
/* We don't generate linked lists for orphan titles. */
|
||||
if (!child_info->app_metadata)
|
||||
{
|
||||
g_titleInfoOrphanCount++;
|
||||
continue;
|
||||
}
|
||||
/* If we have no application metadata, increase orphan title count. */
|
||||
if (!child_info->app_metadata) g_titleInfoOrphanCount++;
|
||||
}
|
||||
|
||||
/* 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. */
|
||||
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) && \
|
||||
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)
|
||||
{
|
||||
TitleApplicationMetadata *tmp_app_metadata = NULL;
|
||||
u32 orig_app_count = g_appMetadataCount, cur_app_count = g_appMetadataCount, gamecard_metadata_count = 0;
|
||||
bool status = false, success = false, cleanup = true;
|
||||
u32 gamecard_app_count = 0, extra_app_count = 0;
|
||||
bool status = false, success = false, cleanup = true, free_entries = false;
|
||||
|
||||
/* Retrieve current gamecard status. */
|
||||
status = (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded);
|
||||
|
@ -1689,10 +1851,35 @@ static bool titleRefreshGameCardTitleInfo(void)
|
|||
goto end;
|
||||
}
|
||||
|
||||
/* Retrieve gamecard application metadata. */
|
||||
/* Get gamecard user application count. */
|
||||
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 : \
|
||||
(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. */
|
||||
if (cur_title_info->app_metadata != NULL || (cur_title_info->app_metadata = titleFindApplicationMetadataByTitleId(app_id)) != NULL) continue;
|
||||
|
||||
/* Reallocate application metadata buffer (if needed). */
|
||||
if (cur_app_count < (g_appMetadataCount + 1))
|
||||
/* Retrieve application metadata pointer. */
|
||||
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));
|
||||
if (!tmp_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 reallocate application metadata buffer! (additional entry).");
|
||||
LOG_MSG("Failed to allocate memory for application metadata entry #%u!", extra_app_count);
|
||||
goto end;
|
||||
}
|
||||
|
||||
g_appMetadata = tmp_app_metadata;
|
||||
tmp_app_metadata = NULL;
|
||||
|
||||
cur_app_count++;
|
||||
/* Set application metadata entry pointer. */
|
||||
g_appMetadata[g_appMetadataCount + extra_app_count] = cur_app_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++]);
|
||||
gamecard_metadata_count++;
|
||||
/* Update application metadata pointer in title info. */
|
||||
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 (gamecard_metadata_count)
|
||||
if (extra_app_count)
|
||||
{
|
||||
/* Update application metadata count. */
|
||||
g_appMetadataCount += extra_app_count;
|
||||
|
||||
/* 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. */
|
||||
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. */
|
||||
for(u32 i = 0; i < g_titleInfoCount; 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;
|
||||
TitleInfo *title_info = g_titleInfo[i];
|
||||
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) : \
|
||||
titleGetApplicationIdByAddOnContentId(title_info->meta_key.id));
|
||||
|
@ -1749,8 +1942,15 @@ static bool titleRefreshGameCardTitleInfo(void)
|
|||
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;
|
||||
cleanup = false;
|
||||
|
||||
|
@ -1758,18 +1958,8 @@ end:
|
|||
/* Update gamecard status. */
|
||||
g_titleGameCardAvailable = status;
|
||||
|
||||
/* Decrease application metadata buffer size (if needed). */
|
||||
if ((success && g_appMetadataCount < cur_app_count) || (!success && g_appMetadataCount > orig_app_count))
|
||||
{
|
||||
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;
|
||||
}
|
||||
}
|
||||
/* Free previously allocated application metadata pointers. Ignore return value. */
|
||||
if (!success && free_entries) titleReallocateApplicationMetadata(extra_app_count, true);
|
||||
|
||||
/* Remove gamecard title info entries and close its ncm database and storage handles (if needed). */
|
||||
if (cleanup)
|
||||
|
@ -1783,45 +1973,31 @@ end:
|
|||
|
||||
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;
|
||||
|
||||
if (g_titleInfoGameCardCount == g_titleInfoCount)
|
||||
{
|
||||
/* Free all title info entries. */
|
||||
titleFreeTitleInfo();
|
||||
} else {
|
||||
/* Free content infos from gamecard title info entries. */
|
||||
for(u32 i = g_titleInfoGameCardStartIndex; i < g_titleInfoCount; i++)
|
||||
{
|
||||
TitleInfo *cur_title_info = &(g_titleInfo[i]);
|
||||
if (cur_title_info->content_infos) free(cur_title_info->content_infos);
|
||||
}
|
||||
|
||||
/* 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();
|
||||
}
|
||||
/* Update title info count. */
|
||||
g_titleInfoCount = g_titleInfoGameCardStartIndex;
|
||||
|
||||
/* Reallocate title info pointer array. */
|
||||
titleReallocateTitleInfo(g_titleInfoGameCardCount - 1, true);
|
||||
|
||||
/* Update gamecard variables. */
|
||||
g_titleInfoGameCardStartIndex = g_titleInfoGameCardCount = 0;
|
||||
|
||||
/* Update linked lists for user applications, patches and add-on contents. */
|
||||
/* Won't do anything if g_titleInfoCount is already zero. */
|
||||
titleUpdateTitleInfoLinkedLists();
|
||||
}
|
||||
|
||||
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++)
|
||||
{
|
||||
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) || \
|
||||
(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;
|
||||
|
||||
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)
|
||||
{
|
||||
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++)
|
||||
{
|
||||
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;
|
||||
break;
|
||||
|
@ -1869,8 +2045,8 @@ end:
|
|||
|
||||
static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *b)
|
||||
{
|
||||
const TitleApplicationMetadata *app_metadata_1 = (const TitleApplicationMetadata*)a;
|
||||
const TitleApplicationMetadata *app_metadata_2 = (const TitleApplicationMetadata*)b;
|
||||
const TitleApplicationMetadata *app_metadata_1 = *((const TitleApplicationMetadata**)a);
|
||||
const TitleApplicationMetadata *app_metadata_2 = *((const TitleApplicationMetadata**)b);
|
||||
|
||||
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)
|
||||
{
|
||||
const TitleApplicationMetadata *app_metadata_1 = (const TitleApplicationMetadata*)a;
|
||||
const TitleApplicationMetadata *app_metadata_2 = (const TitleApplicationMetadata*)b;
|
||||
const TitleApplicationMetadata *app_metadata_1 = *((const TitleApplicationMetadata**)a);
|
||||
const TitleApplicationMetadata *app_metadata_2 = *((const TitleApplicationMetadata**)b);
|
||||
|
||||
return strcasecmp(app_metadata_1->lang_entry.name, app_metadata_2->lang_entry.name);
|
||||
}
|
||||
|
|
|
@ -139,7 +139,9 @@ NX_INLINE void titleConvertNcmContentSizeToU64(const u8 *size, u64 *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)
|
||||
|
|
Loading…
Reference in a new issue