1
0
Fork 0
mirror of https://github.com/DarkMatterCore/nxdumptool.git synced 2024-12-01 22:52:39 +00:00

title: retrieve display version string from patches while generating output filenames.

This commit is contained in:
Pablo Curiel 2021-06-05 17:06:29 -04:00
parent 5bb5f0c858
commit 986b19868f
7 changed files with 113 additions and 36 deletions

View file

@ -200,13 +200,14 @@ typedef enum {
} GameCardStatus; } GameCardStatus;
typedef enum { typedef enum {
GameCardHashFileSystemPartitionType_Root = 0, GameCardHashFileSystemPartitionType_None = 0, ///< Not a real value.
GameCardHashFileSystemPartitionType_Update = 1, GameCardHashFileSystemPartitionType_Root = 1,
GameCardHashFileSystemPartitionType_Logo = 2, ///< Only available in GameCardFwVersion_Since400NUP or greater gamecards. GameCardHashFileSystemPartitionType_Update = 2,
GameCardHashFileSystemPartitionType_Normal = 3, GameCardHashFileSystemPartitionType_Logo = 3, ///< Only available in GameCardFwVersion_Since400NUP or greater gamecards.
GameCardHashFileSystemPartitionType_Secure = 4, GameCardHashFileSystemPartitionType_Normal = 4,
GameCardHashFileSystemPartitionType_Boot = 5, GameCardHashFileSystemPartitionType_Secure = 5,
GameCardHashFileSystemPartitionType_Count = 6 ///< Not a real value. GameCardHashFileSystemPartitionType_Boot = 6,
GameCardHashFileSystemPartitionType_Count = 7 ///< Not a real value.
} GameCardHashFileSystemPartitionType; } GameCardHashFileSystemPartitionType;
/// Initializes data needed to access raw gamecard storage areas. /// Initializes data needed to access raw gamecard storage areas.

View file

@ -130,7 +130,7 @@ void titleFreeOrphanTitles(TitleInfo ***orphan_info);
bool titleIsGameCardInfoUpdated(void); bool titleIsGameCardInfoUpdated(void);
/// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output title dumps. Returns NULL if an error occurs. /// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output title dumps. Returns NULL if an error occurs.
char *titleGenerateFileName(const TitleInfo *title_info, u8 name_convention, u8 illegal_char_replace_type); char *titleGenerateFileName(TitleInfo *title_info, u8 name_convention, u8 illegal_char_replace_type);
/// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output gamecard dumps. Returns NULL if an error occurs. /// Returns a pointer to a dynamically allocated buffer that holds a filename string suitable for output gamecard dumps. Returns NULL if an error occurs.
/// A valid gamecard must be inserted, and title info must have been loaded from it accordingly. /// A valid gamecard must be inserted, and title info must have been loaded from it accordingly.

View file

@ -97,6 +97,7 @@ bool bfttfInitialize(void)
} }
/* Initialize NCA context. */ /* Initialize NCA context. */
/* NCA contexts don't need to be freed beforehand. */
bool nca_ctx_init = ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Data, 0), NULL); bool nca_ctx_init = ncaInitializeContext(nca_ctx, NcmStorageId_BuiltInSystem, 0, titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Data, 0), NULL);
/* Free title info. */ /* Free title info. */

View file

@ -285,7 +285,7 @@ bool cnmtUpdateContentInfo(ContentMetaContext *cnmt_ctx, NcaContext *nca_ctx)
/* Jackpot. Copy content ID and hash to our raw CNMT. */ /* Jackpot. Copy content ID and hash to our raw CNMT. */
memcpy(packaged_content_info->hash, nca_ctx->hash, sizeof(nca_ctx->hash)); memcpy(packaged_content_info->hash, nca_ctx->hash, sizeof(nca_ctx->hash));
memcpy(&(content_info->content_id), &(nca_ctx->content_id), sizeof(NcmContentId)); memcpy(&(content_info->content_id), &(nca_ctx->content_id), sizeof(NcmContentId));
LOG_MSG("Updated CNMT content record #%u (Title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", i, cnmt_ctx->packaged_header->title_id, content_size, content_info->content_type, \ LOG_MSG("Updated CNMT content record #%u (title ID %016lX, size 0x%lX, type 0x%02X, ID offset 0x%02X).", i, cnmt_ctx->packaged_header->title_id, content_size, content_info->content_type, \
content_info->id_offset); content_info->id_offset);
success = true; success = true;
break; break;

View file

@ -35,6 +35,8 @@
#define GAMECARD_STORAGE_AREA_NAME(x) ((x) == GameCardStorageArea_Normal ? "normal" : ((x) == GameCardStorageArea_Secure ? "secure" : "none")) #define GAMECARD_STORAGE_AREA_NAME(x) ((x) == GameCardStorageArea_Normal ? "normal" : ((x) == GameCardStorageArea_Secure ? "secure" : "none"))
#define GAMECARD_HFS_PARTITION_NAME_INDEX(x) ((x) - 1)
#define LAFW_MAGIC 0x4C414657 /* "LAFW". */ #define LAFW_MAGIC 0x4C414657 /* "LAFW". */
/* Type definitions. */ /* Type definitions. */
@ -139,12 +141,12 @@ static MemoryLocation g_fsProgramMemory = {
}; };
static const char *g_gameCardHfsPartitionNames[] = { static const char *g_gameCardHfsPartitionNames[] = {
[GameCardHashFileSystemPartitionType_Root] = "root", [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Root)] = "root",
[GameCardHashFileSystemPartitionType_Update] = "update", [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Update)] = "update",
[GameCardHashFileSystemPartitionType_Logo] = "logo", [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Logo)] = "logo",
[GameCardHashFileSystemPartitionType_Normal] = "normal", [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Normal)] = "normal",
[GameCardHashFileSystemPartitionType_Secure] = "secure", [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Secure)] = "secure",
[GameCardHashFileSystemPartitionType_Boot] = "boot" [GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Boot)] = "boot"
}; };
/* Function prototypes. */ /* Function prototypes. */
@ -424,7 +426,7 @@ bool gamecardGetBundledFirmwareUpdateVersion(VersionType1 *out)
bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out) bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemContext *out)
{ {
if (hfs_partition_type >= GameCardHashFileSystemPartitionType_Count || !out) if (!hfs_partition_type || hfs_partition_type >= GameCardHashFileSystemPartitionType_Count || !out)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
@ -474,7 +476,7 @@ bool gamecardGetHashFileSystemContext(u8 hfs_partition_type, HashFileSystemConte
bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size) bool gamecardGetHashFileSystemEntryInfoByName(u8 hfs_partition_type, const char *entry_name, u64 *out_offset, u64 *out_size)
{ {
if (!entry_name || !*entry_name || (!out_offset && !out_size)) if (!hfs_partition_type || hfs_partition_type >= GameCardHashFileSystemPartitionType_Count || !entry_name || !*entry_name || (!out_offset && !out_size))
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;
@ -1163,7 +1165,7 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char
} }
/* Duplicate partition name. */ /* Duplicate partition name. */
fs_ctx->name = (name ? strdup(name) : strdup(g_gameCardHfsPartitionNames[GameCardHashFileSystemPartitionType_Root])); fs_ctx->name = (name ? strdup(name) : strdup(g_gameCardHfsPartitionNames[GAMECARD_HFS_PARTITION_NAME_INDEX(GameCardHashFileSystemPartitionType_Root)]));
if (!fs_ctx->name) if (!fs_ctx->name)
{ {
LOG_MSG("Failed to duplicate Hash FS partition name! (offset 0x%lX).", offset); LOG_MSG("Failed to duplicate Hash FS partition name! (offset 0x%lX).", offset);
@ -1173,7 +1175,7 @@ static HashFileSystemContext *gamecardInitializeHashFileSystemContext(const char
/* Determine Hash FS partition type. */ /* Determine Hash FS partition type. */
for(i = GameCardHashFileSystemPartitionType_Root; i < GameCardHashFileSystemPartitionType_Count; i++) for(i = GameCardHashFileSystemPartitionType_Root; i < GameCardHashFileSystemPartitionType_Count; i++)
{ {
if (!strcmp(g_gameCardHfsPartitionNames[i], fs_ctx->name)) break; if (!strcmp(g_gameCardHfsPartitionNames[GAMECARD_HFS_PARTITION_NAME_INDEX(i)], fs_ctx->name)) break;
} }
if (i >= GameCardHashFileSystemPartitionType_Count) if (i >= GameCardHashFileSystemPartitionType_Count)
@ -1273,9 +1275,9 @@ end:
static HashFileSystemContext *_gamecardGetHashFileSystemContext(u8 hfs_partition_type) static HashFileSystemContext *_gamecardGetHashFileSystemContext(u8 hfs_partition_type)
{ {
HashFileSystemContext *fs_ctx = NULL; HashFileSystemContext *fs_ctx = NULL;
const char *partition_name = NULL;
if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !g_gameCardHfsCount || !g_gameCardHfsCtx || hfs_partition_type >= GameCardHashFileSystemPartitionType_Count) if (!g_gameCardInterfaceInit || g_gameCardStatus != GameCardStatus_InsertedAndInfoLoaded || !g_gameCardHfsCount || !g_gameCardHfsCtx || !hfs_partition_type || \
hfs_partition_type >= GameCardHashFileSystemPartitionType_Count)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
goto end; goto end;
@ -1288,18 +1290,15 @@ static HashFileSystemContext *_gamecardGetHashFileSystemContext(u8 hfs_partition
goto end; goto end;
} }
/* Get requested partition name. */
partition_name = g_gameCardHfsPartitionNames[hfs_partition_type];
/* Try to find the requested partition by looping through our Hash FS contexts. */ /* Try to find the requested partition by looping through our Hash FS contexts. */
for(u32 i = 1; i < g_gameCardHfsCount; i++) for(u32 i = 1; i < g_gameCardHfsCount; i++)
{ {
fs_ctx = g_gameCardHfsCtx[i]; fs_ctx = g_gameCardHfsCtx[i];
if (!strcmp(fs_ctx->name, partition_name)) break; if (fs_ctx->type == hfs_partition_type) break;
fs_ctx = NULL; fs_ctx = NULL;
} }
if (!fs_ctx) LOG_MSG("Failed to locate Hash FS partition \"%s\"!", partition_name); if (!fs_ctx) LOG_MSG("Failed to locate Hash FS partition with type %u!", hfs_partition_type);
end: end:
return fs_ctx; return fs_ctx;

View file

@ -95,7 +95,8 @@ bool ncaInitializeContext(NcaContext *out, u8 storage_id, u8 hfs_partition_type,
u8 valid_fs_section_cnt = 0; u8 valid_fs_section_cnt = 0;
if (!out || (storage_id != NcmStorageId_GameCard && !(ncm_storage = titleGetNcmStorageByStorageId(storage_id))) || \ if (!out || (storage_id != NcmStorageId_GameCard && !(ncm_storage = titleGetNcmStorageByStorageId(storage_id))) || \
(storage_id == NcmStorageId_GameCard && hfs_partition_type >= GameCardHashFileSystemPartitionType_Count) || !content_info || content_info->content_type > NcmContentType_DeltaFragment) (storage_id == NcmStorageId_GameCard && (!hfs_partition_type || hfs_partition_type >= GameCardHashFileSystemPartitionType_Count)) || !content_info || \
content_info->content_type > NcmContentType_DeltaFragment)
{ {
LOG_MSG("Invalid parameters!"); LOG_MSG("Invalid parameters!");
return false; return false;

View file

@ -22,6 +22,7 @@
#include "nxdt_utils.h" #include "nxdt_utils.h"
#include "title.h" #include "title.h"
#include "gamecard.h" #include "gamecard.h"
#include "nacp.h"
#define NS_APPLICATION_RECORD_LIMIT 4096 #define NS_APPLICATION_RECORD_LIMIT 4096
@ -465,6 +466,8 @@ static int titleSystemTitleMetadataEntrySortFunction(const void *a, const void *
static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b); static int titleUserApplicationMetadataEntrySortFunction(const void *a, const void *b);
static int titleOrphanTitleInfoSortFunction(const void *a, const void *b); static int titleOrphanTitleInfoSortFunction(const void *a, const void *b);
static char *titleGetPatchVersionString(TitleInfo *title_info);
bool titleInitialize(void) bool titleInitialize(void)
{ {
bool ret = false; bool ret = false;
@ -869,7 +872,7 @@ bool titleIsGameCardInfoUpdated(void)
return ret; return ret;
} }
char *titleGenerateFileName(const TitleInfo *title_info, u8 name_convention, u8 illegal_char_replace_type) char *titleGenerateFileName(TitleInfo *title_info, u8 name_convention, u8 illegal_char_replace_type)
{ {
if (!title_info || title_info->meta_key.type < NcmContentMetaType_Application || title_info->meta_key.type > NcmContentMetaType_Delta || name_convention > TitleFileNameConvention_IdAndVersionOnly || \ if (!title_info || title_info->meta_key.type < NcmContentMetaType_Application || title_info->meta_key.type > NcmContentMetaType_Delta || name_convention > TitleFileNameConvention_IdAndVersionOnly || \
(name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly)) (name_convention == TitleFileNameConvention_Full && illegal_char_replace_type > TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly))
@ -878,15 +881,24 @@ char *titleGenerateFileName(const TitleInfo *title_info, u8 name_convention, u8
return NULL; return NULL;
} }
char title_name[0x400] = {0}, *filename = NULL;
u8 type = (title_info->meta_key.type - 0x80); u8 type = (title_info->meta_key.type - 0x80);
char title_name[0x400] = {0}, *version_str = NULL, *filename = NULL;
/* Generate filename for this title. */ /* Generate filename for this title. */
if (name_convention == TitleFileNameConvention_Full) if (name_convention == TitleFileNameConvention_Full)
{ {
if (title_info->app_metadata && *(title_info->app_metadata->lang_entry.name)) if (title_info->app_metadata && *(title_info->app_metadata->lang_entry.name))
{ {
/* Retrieve display version string if we're dealing with a Patch. */
if (title_info->meta_key.type == NcmContentMetaType_Patch) version_str = titleGetPatchVersionString(title_info);
sprintf(title_name, "%s ", title_info->app_metadata->lang_entry.name); sprintf(title_name, "%s ", title_info->app_metadata->lang_entry.name);
if (version_str)
{
sprintf(title_name + strlen(title_name), "%s ", version_str);
free(version_str);
}
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(title_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); if (illegal_char_replace_type) utilsReplaceIllegalCharacters(title_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly);
} }
@ -923,12 +935,12 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
GameCardHeader gc_header = {0}; GameCardHeader gc_header = {0};
size_t cur_filename_len = 0; size_t cur_filename_len = 0;
char app_name[0x400] = {0}, *tmp_filename = NULL; char app_name[0x400] = {0};
bool error = false; bool error = false;
for(u32 i = 0; i < title_count; i++) for(u32 i = 0; i < title_count; i++)
{ {
TitleInfo *app_info = titles[i]; TitleInfo *app_info = titles[i], *patch_info = NULL;
if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue; if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue;
u32 app_version = app_info->meta_key.version; u32 app_version = app_info->meta_key.version;
@ -939,11 +951,12 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
{ {
if (j == i) continue; if (j == i) continue;
TitleInfo *patch_info = titles[j]; TitleInfo *cur_title_info = titles[j];
if (!patch_info || patch_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, patch_info->meta_key.id) || \ if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_Patch || !titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id) || \
patch_info->meta_key.version <= app_version) continue; cur_title_info->meta_key.version <= app_version) continue;
app_version = patch_info->meta_key.version; patch_info = cur_title_info;
app_version = cur_title_info->meta_key.version;
} }
/* Generate current user application name. */ /* Generate current user application name. */
@ -955,7 +968,17 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
if (app_info->app_metadata && *(app_info->app_metadata->lang_entry.name)) if (app_info->app_metadata && *(app_info->app_metadata->lang_entry.name))
{ {
/* Retrieve display version string if the inserted gamecard holds a patch for the current user application. */
char *version_str = NULL;
if (patch_info) version_str = titleGetPatchVersionString(patch_info);
sprintf(app_name + strlen(app_name), "%s ", app_info->app_metadata->lang_entry.name); sprintf(app_name + strlen(app_name), "%s ", app_info->app_metadata->lang_entry.name);
if (version_str)
{
sprintf(app_name + strlen(app_name), "%s ", version_str);
free(version_str);
}
if (illegal_char_replace_type) utilsReplaceIllegalCharacters(app_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly); if (illegal_char_replace_type) utilsReplaceIllegalCharacters(app_name, illegal_char_replace_type == TitleFileNameIllegalCharReplaceType_KeepAsciiCharsOnly);
} }
@ -970,7 +993,7 @@ char *titleGenerateGameCardFileName(u8 name_convention, u8 illegal_char_replace_
/* Reallocate output buffer. */ /* Reallocate output buffer. */
size_t app_name_len = strlen(app_name); size_t app_name_len = strlen(app_name);
tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char)); char *tmp_filename = realloc(filename, (cur_filename_len + app_name_len + 1) * sizeof(char));
if (!tmp_filename) if (!tmp_filename)
{ {
LOG_MSG("Failed to reallocate filename buffer!"); LOG_MSG("Failed to reallocate filename buffer!");
@ -2403,3 +2426,55 @@ static int titleOrphanTitleInfoSortFunction(const void *a, const void *b)
return 0; return 0;
} }
static char *titleGetPatchVersionString(TitleInfo *title_info)
{
NcmContentInfo *nacp_content = NULL;
u8 storage_id = 0, hfs_partition_type = 0;
NcaContext *nca_ctx = NULL;
NacpContext nacp_ctx = {0};
char *str = NULL;
if (!title_info || title_info->meta_key.type != NcmContentMetaType_Patch || !(nacp_content = titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Control, 0)))
{
LOG_MSG("Invalid parameters!");
goto end;
}
/* Update parameters. */
storage_id = title_info->storage_id;
if (storage_id == NcmStorageId_GameCard) hfs_partition_type = GameCardHashFileSystemPartitionType_Secure;
/* Allocate memory for the NCA context. */
nca_ctx = calloc(1, sizeof(NcaContext));
if (!nca_ctx)
{
LOG_MSG("Failed to allocate memory for NCA context!");
goto end;
}
/* Initialize NCA context. */
if (!ncaInitializeContext(nca_ctx, storage_id, hfs_partition_type, nacp_content, NULL))
{
LOG_MSG("Failed to initialize NCA context for Control NCA from %016lX!", title_info->meta_key.id);
goto end;
}
/* Initialize NACP context. */
if (!nacpInitializeContext(&nacp_ctx, nca_ctx))
{
LOG_MSG("Failed to initialize NACP context for %016lX!", title_info->meta_key.id);
goto end;
}
/* Get version string. */
str = strndup(nacp_ctx.data->display_version, sizeof(nacp_ctx.data->display_version));
if (!str) LOG_MSG("Failed to duplicate version string from %016lX!", title_info->meta_key.id);
end:
nacpFreeContext(&nacp_ctx);
if (nca_ctx) free(nca_ctx);
return str;
}