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)
{
if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && !titleIsGameCardInfoUpdated()) break;
if (gamecardGetStatus() == GameCardStatus_InsertedAndInfoLoaded && titleIsGameCardInfoUpdated()) break;
}
consolePrint("waiting for usb session...\n");

View file

@ -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))
{

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.
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.

View file

@ -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)

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) && \
(((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)
{

View file

@ -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);
}

View file

@ -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)