1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-11-09 12:11:44 +00:00

title: pre-generate gamecard filenames

Changes include:

* title: define a global string array for the pre-generated gamecard filenames.
* title: move gamecard filename generation logic into a new function, _titleGenerateGameCardFileName().
* title: update background gamecard thread logic to make it generate gamecard filenames on any status changes.
* title: update titleGenerateFileName() to make it return a duplicate of the gamecard filename requested by the caller.
This commit is contained in:
Pablo Curiel 2024-08-11 12:59:22 +02:00
parent 00497b5181
commit 5a40167a13
2 changed files with 149 additions and 100 deletions

View file

@ -35,18 +35,16 @@ typedef struct {
u8 type; ///< NsoSegmentType. u8 type; ///< NsoSegmentType.
const char *name; ///< Pointer to a string that holds the segment name. const char *name; ///< Pointer to a string that holds the segment name.
NsoSegmentInfo info; ///< Copied from the NSO header. NsoSegmentInfo info; ///< Copied from the NSO header.
u8 *data; ///< Dynamically allocated buffer with the decompressed segment data. u8 *data; ///< Dynamically allocated buffer for the decompressed segment data.
} NsoSegment; } NsoSegment;
/* Global variables. */ /* Global variables. */
#if LOG_LEVEL < LOG_LEVEL_NONE
static const char *g_nsoSegmentTypeNames[NsoSegmentType_Count] = { static const char *g_nsoSegmentTypeNames[NsoSegmentType_Count] = {
[NsoSegmentType_Text] = ".text", [NsoSegmentType_Text] = ".text",
[NsoSegmentType_RoData] = ".rodata", [NsoSegmentType_RoData] = ".rodata",
[NsoSegmentType_Data] = ".data", [NsoSegmentType_Data] = ".data",
}; };
#endif
/* Function prototypes. */ /* Function prototypes. */

View file

@ -65,6 +65,8 @@ static u32 g_filteredSystemMetadataCount = 0, g_filteredUserMetadataCount = 0;
static TitleGameCardApplicationMetadata *g_titleGameCardApplicationMetadata = NULL; static TitleGameCardApplicationMetadata *g_titleGameCardApplicationMetadata = NULL;
static u32 g_titleGameCardApplicationMetadataCount = 0; static u32 g_titleGameCardApplicationMetadataCount = 0;
static char *g_titleGameCardFileNames[TitleNamingConvention_Count] = {NULL};
static TitleStorage g_titleStorage[TITLE_STORAGE_COUNT] = {0}; static TitleStorage g_titleStorage[TITLE_STORAGE_COUNT] = {0};
static TitleInfo **g_orphanTitleInfo = NULL; static TitleInfo **g_orphanTitleInfo = NULL;
@ -577,6 +579,10 @@ static void titleGameCardInfoThreadFunc(void *arg);
static bool titleRefreshGameCardTitleInfo(void); static bool titleRefreshGameCardTitleInfo(void);
NX_INLINE void titleFreeGameCardFileNames(void);
NX_INLINE void titleGenerateGameCardFileNames(void);
static char *_titleGenerateGameCardFileName(u8 naming_convention);
static bool titleIsUserApplicationContentAvailable(u64 app_id); static bool titleIsUserApplicationContentAvailable(u64 app_id);
static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id); static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
@ -1060,8 +1066,7 @@ bool titleIsGameCardInfoUpdated(void)
char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 illegal_char_replace_type) char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 illegal_char_replace_type)
{ {
if (!title_info || (title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \ if (!title_info || (title_info->meta_key.type > NcmContentMetaType_BootImagePackageSafe && title_info->meta_key.type < NcmContentMetaType_Application) || \
title_info->meta_key.type > NcmContentMetaType_DataPatch || naming_convention > TitleNamingConvention_IdAndVersionOnly || \ title_info->meta_key.type > NcmContentMetaType_DataPatch || naming_convention >= TitleNamingConvention_Count || illegal_char_replace_type >= TitleFileNameIllegalCharReplaceType_Count)
(naming_convention == TitleNamingConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
{ {
LOG_MSG_ERROR("Invalid parameters!"); LOG_MSG_ERROR("Invalid parameters!");
return NULL; return NULL;
@ -1114,101 +1119,23 @@ char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replac
SCOPED_LOCK(&g_titleMutex) SCOPED_LOCK(&g_titleMutex)
{ {
GameCardHeader gc_header = {0}; if (!g_titleInterfaceInit || !g_titleGameCardAvailable || naming_convention >= TitleNamingConvention_Count || \
char app_name[0x300] = {0}; illegal_char_replace_type >= TitleFileNameIllegalCharReplaceType_Count || !g_titleGameCardFileNames[naming_convention])
size_t cur_filename_len = 0, app_name_len = 0;
bool error = false;
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || naming_convention > TitleNamingConvention_IdAndVersionOnly || \
(naming_convention == TitleNamingConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
{ {
LOG_MSG_ERROR("Invalid parameters!"); LOG_MSG_ERROR("Invalid parameters!");
break; break;
} }
LOG_MSG_DEBUG("Generating %s gamecard filename...", naming_convention == TitleNamingConvention_Full ? "full" : "ID and version"); /* Duplicate generated filename. */
filename = strdup(g_titleGameCardFileNames[naming_convention]);
/* Check if we don't have any gamecard application metadata records we can work with. */ if (!filename)
/* This is especially true for Kiosk / Quest gamecards. */
if (!g_titleGameCardApplicationMetadata || !g_titleGameCardApplicationMetadataCount) goto fallback;
/* Loop through our gamecard application metadata entries. */
for(u32 i = 0; i < g_titleGameCardApplicationMetadataCount; i++)
{ {
const TitleGameCardApplicationMetadata *cur_gc_app_metadata = &(g_titleGameCardApplicationMetadata[i]); LOG_MSG_ERROR("Failed to duplicate generated filename!");
break;
/* Generate current user application name. */
*app_name = '\0';
if (naming_convention == TitleNamingConvention_Full)
{
if (cur_filename_len) strcat(app_name, " + ");
if (cur_gc_app_metadata->app_metadata && cur_gc_app_metadata->app_metadata->lang_entry.name[0])
{
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->app_metadata->lang_entry.name);
/* Append display version string if the inserted gamecard holds a patch for the current user application. */
if (cur_gc_app_metadata->has_patch && cur_gc_app_metadata->display_version[0])
{
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->display_version);
}
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(app_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly);
}
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "[%016lX][v%u]", cur_gc_app_metadata->app_metadata->title_id, cur_gc_app_metadata->version.value);
} else
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
{
if (cur_filename_len) strcat(app_name, "+");
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%016lX_v%u", cur_gc_app_metadata->app_metadata->title_id, cur_gc_app_metadata->version.value);
}
/* Reallocate output buffer. */
app_name_len = strlen(app_name);
char *tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char));
if (!tmp_filename)
{
LOG_MSG_ERROR("Failed to reallocate filename buffer!");
if (filename) free(filename);
filename = NULL;
error = true;
break;
}
filename = tmp_filename;
tmp_filename = NULL;
/* Concatenate current user application name. */
filename[cur_filename_len] = '\0';
strcat(filename, app_name);
cur_filename_len += app_name_len;
} }
fallback: /* Replace illegal characters, if requested. */
if (!filename && !error) if (illegal_char_replace_type) utilsReplaceIllegalCharacters(filename, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly);
{
LOG_MSG_ERROR("Error: the inserted gamecard doesn't hold any user applications!");
/* Fallback string if no applications can be found. */
sprintf(app_name, "gamecard");
if (gamecardGetHeader(&gc_header))
{
strcat(app_name, "_");
cur_filename_len = strlen(app_name);
utilsGenerateHexString(app_name + cur_filename_len, sizeof(app_name) - cur_filename_len, gc_header.package_id, sizeof(gc_header.package_id), false);
}
filename = strdup(app_name);
if (!filename) LOG_MSG_ERROR("Failed to duplicate fallback filename!");
}
} }
return filename; return filename;
@ -2491,20 +2418,23 @@ static void titleGameCardInfoThreadFunc(void *arg)
{ {
g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo(); g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo();
if (g_titleGameCardInfoUpdated) /* Generate gamecard application metadata array. */
{ titleGenerateGameCardApplicationMetadataArray();
/* Generate filtered user application metadata pointer array. */
titleGenerateFilteredApplicationMetadataPointerArray(false);
/* Generate gamecard application metadata array. */ /* Generate gamecard file names. */
titleGenerateGameCardApplicationMetadataArray(); titleGenerateGameCardFileNames();
}
/* Generate filtered user application metadata pointer array. */
if (g_titleGameCardInfoUpdated) titleGenerateFilteredApplicationMetadataPointerArray(false);
} }
} }
/* Update gamecard flags. */ /* Update gamecard flags. */
g_titleGameCardAvailable = g_titleGameCardInfoUpdated = false; g_titleGameCardAvailable = g_titleGameCardInfoUpdated = false;
/* Free gamecard file names. */
titleFreeGameCardFileNames();
threadExit(); threadExit();
} }
@ -2647,6 +2577,127 @@ end:
return success; return success;
} }
NX_INLINE void titleFreeGameCardFileNames(void)
{
for(u8 i = 0; i < TitleNamingConvention_Count; i++)
{
if (g_titleGameCardFileNames[i])
{
free(g_titleGameCardFileNames[i]);
g_titleGameCardFileNames[i] = NULL;
}
}
}
NX_INLINE void titleGenerateGameCardFileNames(void)
{
titleFreeGameCardFileNames();
if (g_titleGameCardAvailable)
{
for(u8 i = 0; i < TitleNamingConvention_Count; i++) g_titleGameCardFileNames[i] = _titleGenerateGameCardFileName(i);
}
}
static char *_titleGenerateGameCardFileName(u8 naming_convention)
{
if (naming_convention >= TitleNamingConvention_Count)
{
LOG_MSG_ERROR("Invalid parameters!");
return NULL;
}
char *filename = NULL;
GameCardHeader gc_header = {0};
char app_name[0x300] = {0};
size_t cur_filename_len = 0, app_name_len = 0;
bool error = false;
LOG_MSG_DEBUG("Generating %s gamecard filename...", naming_convention == TitleNamingConvention_Full ? "full" : "ID and version");
/* Check if we don't have any gamecard application metadata records we can work with. */
/* This is especially true for Kiosk / Quest gamecards. */
if (!g_titleGameCardApplicationMetadata || !g_titleGameCardApplicationMetadataCount) goto fallback;
/* Loop through our gamecard application metadata entries. */
for(u32 i = 0; i < g_titleGameCardApplicationMetadataCount; i++)
{
const TitleGameCardApplicationMetadata *cur_gc_app_metadata = &(g_titleGameCardApplicationMetadata[i]);
/* Generate current user application name. */
*app_name = '\0';
if (naming_convention == TitleNamingConvention_Full)
{
if (cur_filename_len) strcat(app_name, " + ");
if (cur_gc_app_metadata->app_metadata && cur_gc_app_metadata->app_metadata->lang_entry.name[0])
{
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->app_metadata->lang_entry.name);
/* Append display version string if the inserted gamecard holds a patch for the current user application. */
if (cur_gc_app_metadata->has_patch && cur_gc_app_metadata->display_version[0])
{
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->display_version);
}
}
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "[%016lX][v%u]", cur_gc_app_metadata->app_metadata->title_id, cur_gc_app_metadata->version.value);
} else
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
{
if (cur_filename_len) strcat(app_name, "+");
app_name_len = strlen(app_name);
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%016lX_v%u", cur_gc_app_metadata->app_metadata->title_id, cur_gc_app_metadata->version.value);
}
/* Reallocate output buffer. */
app_name_len = strlen(app_name);
char *tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char));
if (!tmp_filename)
{
LOG_MSG_ERROR("Failed to reallocate filename buffer!");
if (filename) free(filename);
filename = NULL;
error = true;
break;
}
filename = tmp_filename;
tmp_filename = NULL;
/* Concatenate current user application name. */
filename[cur_filename_len] = '\0';
strcat(filename, app_name);
cur_filename_len += app_name_len;
}
fallback:
if (!filename && !error)
{
LOG_MSG_ERROR("Error: the inserted gamecard doesn't hold any user applications!");
/* Fallback string if no applications can be found. */
sprintf(app_name, "gamecard");
if (gamecardGetHeader(&gc_header))
{
strcat(app_name, "_");
cur_filename_len = strlen(app_name);
utilsGenerateHexString(app_name + cur_filename_len, sizeof(app_name) - cur_filename_len, gc_header.package_id, sizeof(gc_header.package_id), false);
}
filename = strdup(app_name);
if (!filename) LOG_MSG_ERROR("Failed to duplicate fallback filename!");
}
return filename;
}
static bool titleIsUserApplicationContentAvailable(u64 app_id) static bool titleIsUserApplicationContentAvailable(u64 app_id)
{ {
if (!app_id) return false; if (!app_id) return false;
@ -2704,7 +2755,7 @@ static TitleInfo *_titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
if (out) break; if (out) break;
} }
if (!out) LOG_MSG_DEBUG("Unable to find title info entry with ID \"%016lX\" in %s.", title_id, titleGetNcmStorageIdName(storage_id)); if (!out && storage_id != NcmStorageId_BuiltInSystem) LOG_MSG_DEBUG("Unable to find title info entry with ID \"%016lX\" in %s.", title_id, titleGetNcmStorageIdName(storage_id));
return out; return out;
} }