mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 10:16:39 +00:00
title: migrate application metadata filtering logic to background thread
titleGetApplicationMetadataEntries() and titleGetGameCardApplicationMetadataEntries() will now return dynamically allocated copies of internal pre-filtered / pre-processed arrays, which are generated using the background gamecard thread. This results in less overhead for any potential calls to these functions. Other changes include: * title: rename TitleGameCardApplicationMetadataEntry -> TitleGameCardApplicationMetadata. * title: add `has_patch` field to TitleGameCardApplicationMetadata struct. * title: declare internal TitleApplicationMetadata arrays to hold pre-filtered application metadata. * title: declare internal TitleGameCardApplicationMetadata array to hold pre-processed gamecard application metadata. * title: move filtering logic from titleGetApplicationMetadataEntries() to a new function: titleGenerateFilteredApplicationMetadataPointerArray(). * title: move processing logic from titleGetGameCardApplicationMetadataEntries() to a new function: titleGenerateGameCardApplicationMetadataArray(). * title: rename titleGetPatchVersionString() -> titleGetDisplayVersionString(). * title: add extra debug log messages to some functions. * title: update titleFreeApplicationMetadata() to also free the new internal metadata arrays. * title: update background thread logic in titleGameCardInfoThreadFunc() to also regenerate the pre-filtered application metadata and gamecard application metadata arrays right after a successful call to titleRefreshGameCardTitleInfo(). * title: update titleGetDisplayVersionString() to also support base application titles. * title: simplify string generation logic in titleGenerateGameCardFileName() by using the cached gamecard application metadata array. * GameCardStatusTask: add GetGameCardStatus() method. * GameCardTab: fix callback argument type in class constructor. * GameCardTab: update ProcessGameCardStatus() to block user inputs while processing the new gamecard status. * RootView: add GetGameCardStatus() method. * StatusInfoTask: turn IsInternetConnectionAvailable() into an inline method. * TitleMetadataTask: turn GetApplicationMetadata() into an inline method. * TitleMetadataTask: move debug log messages around. * TitlesTab: update PopulateList() to block user inputs while updating the titles list. * UmsTask: turn GetUmsDevices() into an inline method. * UsbHostTask: turn GetUsbHostSpeed() into an inline method.
This commit is contained in:
parent
6acdb38d11
commit
5cc387c9b6
17 changed files with 351 additions and 215 deletions
|
@ -49,10 +49,11 @@ typedef struct {
|
||||||
/// Used to display gamecard-specific title information.
|
/// Used to display gamecard-specific title information.
|
||||||
typedef struct {
|
typedef struct {
|
||||||
TitleApplicationMetadata *app_metadata; ///< User application metadata.
|
TitleApplicationMetadata *app_metadata; ///< User application metadata.
|
||||||
Version version; ///< Reflects the title version stored in the inserted gamecard.
|
bool has_patch; ///< Set to true if a patch is also available in the inserted gamecard for this user application.
|
||||||
char display_version[32]; ///< Reflects the title display version stored in its NACP.
|
Version version; ///< Reflects the title version stored in the inserted gamecard, either from a base application or a patch.
|
||||||
|
char display_version[32]; ///< Reflects the title display version from the NACP belonging to either a base application or a patch.
|
||||||
u32 dlc_count; ///< Reflects the number of DLCs available for this application in the inserted gamecard.
|
u32 dlc_count; ///< Reflects the number of DLCs available for this application in the inserted gamecard.
|
||||||
} TitleGameCardApplicationMetadataEntry;
|
} TitleGameCardApplicationMetadata;
|
||||||
|
|
||||||
/// Generated using ncm calls.
|
/// Generated using ncm calls.
|
||||||
/// User applications: the previous/next pointers reference other user applications with the same ID.
|
/// User applications: the previous/next pointers reference other user applications with the same ID.
|
||||||
|
@ -113,10 +114,10 @@ NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id);
|
||||||
/// The allocated buffer must be freed by the caller using free().
|
/// The allocated buffer must be freed by the caller using free().
|
||||||
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count);
|
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count);
|
||||||
|
|
||||||
/// Returns a pointer to a dynamically allocated array of TitleGameCardApplicationMetadataEntry elements generated from gamecard user titles, as well as their count.
|
/// Returns a pointer to a dynamically allocated array of TitleGameCardApplicationMetadata elements generated from gamecard user titles, as well as their count.
|
||||||
/// Returns NULL if an error occurs.
|
/// Returns NULL if an error occurs.
|
||||||
/// The allocated buffer must be freed by the caller using free().
|
/// The allocated buffer must be freed by the caller using free().
|
||||||
TitleGameCardApplicationMetadataEntry *titleGetGameCardApplicationMetadataEntries(u32 *out_count);
|
TitleGameCardApplicationMetadata *titleGetGameCardApplicationMetadataEntries(u32 *out_count);
|
||||||
|
|
||||||
/// Returns a pointer to a dynamically allocated TitleInfo element with a matching storage ID and title ID. Returns NULL if an error occurs.
|
/// Returns a pointer to a dynamically allocated TitleInfo element with a matching storage ID and title ID. Returns NULL if an error occurs.
|
||||||
/// If NcmStorageId_Any is used, the first entry with a matching title ID is returned.
|
/// If NcmStorageId_Any is used, the first entry with a matching title ID is returned.
|
||||||
|
|
|
@ -51,6 +51,12 @@ namespace nxdt::tasks
|
||||||
GameCardStatusTask();
|
GameCardStatusTask();
|
||||||
~GameCardStatusTask();
|
~GameCardStatusTask();
|
||||||
|
|
||||||
|
/* Intentionally left here to let views retrieve title metadata on-demand. */
|
||||||
|
ALWAYS_INLINE const GameCardStatus& GetGameCardStatus(void)
|
||||||
|
{
|
||||||
|
return this->cur_gc_status;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE GameCardStatusEvent::Subscription RegisterListener(GameCardStatusEvent::Callback cb)
|
ALWAYS_INLINE GameCardStatusEvent::Subscription RegisterListener(GameCardStatusEvent::Callback cb)
|
||||||
{
|
{
|
||||||
return this->gc_status_event.subscribe(cb);
|
return this->gc_status_event.subscribe(cb);
|
||||||
|
|
|
@ -58,7 +58,10 @@ namespace nxdt::tasks
|
||||||
StatusInfoTask();
|
StatusInfoTask();
|
||||||
~StatusInfoTask();
|
~StatusInfoTask();
|
||||||
|
|
||||||
bool IsInternetConnectionAvailable(void);
|
ALWAYS_INLINE bool IsInternetConnectionAvailable(void)
|
||||||
|
{
|
||||||
|
return this->status_info_data.connected;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback cb)
|
ALWAYS_INLINE StatusInfoEvent::Subscription RegisterListener(StatusInfoEvent::Callback cb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -57,7 +57,10 @@ namespace nxdt::tasks
|
||||||
~TitleMetadataTask();
|
~TitleMetadataTask();
|
||||||
|
|
||||||
/* Intentionally left here to let views retrieve title metadata on-demand. */
|
/* Intentionally left here to let views retrieve title metadata on-demand. */
|
||||||
const TitleApplicationMetadataVector& GetApplicationMetadata(bool is_system);
|
ALWAYS_INLINE const TitleApplicationMetadataVector& GetApplicationMetadata(bool is_system)
|
||||||
|
{
|
||||||
|
return (is_system ? this->system_metadata : this->user_metadata);
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE UserTitleEvent::Subscription RegisterListener(UserTitleEvent::Callback cb)
|
ALWAYS_INLINE UserTitleEvent::Subscription RegisterListener(UserTitleEvent::Callback cb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -60,7 +60,10 @@ namespace nxdt::tasks
|
||||||
~UmsTask();
|
~UmsTask();
|
||||||
|
|
||||||
/* Intentionally left here to let views retrieve UMS device info on-demand. */
|
/* Intentionally left here to let views retrieve UMS device info on-demand. */
|
||||||
const UmsDeviceVector& GetUmsDevices(void);
|
ALWAYS_INLINE const UmsDeviceVector& GetUmsDevices(void)
|
||||||
|
{
|
||||||
|
return this->ums_devices_vector;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback cb)
|
ALWAYS_INLINE UmsEvent::Subscription RegisterListener(UmsEvent::Callback cb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -50,7 +50,10 @@ namespace nxdt::tasks
|
||||||
~UsbHostTask();
|
~UsbHostTask();
|
||||||
|
|
||||||
/* Intentionally left here to let views retrieve USB host connection speed on-demand. */
|
/* Intentionally left here to let views retrieve USB host connection speed on-demand. */
|
||||||
const UsbHostSpeed& GetUsbHostSpeed(void);
|
ALWAYS_INLINE const UsbHostSpeed& GetUsbHostSpeed(void)
|
||||||
|
{
|
||||||
|
return this->cur_usb_host_speed;
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE UsbHostEvent::Subscription RegisterListener(UsbHostEvent::Callback cb)
|
ALWAYS_INLINE UsbHostEvent::Subscription RegisterListener(UsbHostEvent::Callback cb)
|
||||||
{
|
{
|
||||||
|
|
|
@ -43,9 +43,7 @@ namespace nxdt::views
|
||||||
std::string raw_filename_full = "";
|
std::string raw_filename_full = "";
|
||||||
std::string raw_filename_id_only = "";
|
std::string raw_filename_id_only = "";
|
||||||
|
|
||||||
void ProcessGameCardStatus(GameCardStatus gc_status);
|
void ProcessGameCardStatus(const GameCardStatus& gc_status);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
void PopulateList(void);
|
void PopulateList(void);
|
||||||
void AddApplicationMetadataItems(void);
|
void AddApplicationMetadataItems(void);
|
||||||
|
@ -55,8 +53,6 @@ namespace nxdt::views
|
||||||
std::string GetFormattedSizeString(GameCardSizeFunc func);
|
std::string GetFormattedSizeString(GameCardSizeFunc func);
|
||||||
std::string GetCardIdSetString(const FsGameCardIdSet& card_id_set);
|
std::string GetCardIdSetString(const FsGameCardIdSet& card_id_set);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
GameCardTab(RootView *root_view);
|
GameCardTab(RootView *root_view);
|
||||||
~GameCardTab();
|
~GameCardTab();
|
||||||
|
|
|
@ -90,6 +90,11 @@ namespace nxdt::views
|
||||||
return this->status_info_task->IsInternetConnectionAvailable();
|
return this->status_info_task->IsInternetConnectionAvailable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ALWAYS_INLINE const GameCardStatus& GetGameCardStatus(void)
|
||||||
|
{
|
||||||
|
return this->gc_status_task->GetGameCardStatus();
|
||||||
|
}
|
||||||
|
|
||||||
ALWAYS_INLINE const nxdt::tasks::TitleApplicationMetadataVector& GetApplicationMetadata(bool is_system)
|
ALWAYS_INLINE const nxdt::tasks::TitleApplicationMetadataVector& GetApplicationMetadata(bool is_system)
|
||||||
{
|
{
|
||||||
return this->title_metadata_task->GetApplicationMetadata(is_system);
|
return this->title_metadata_task->GetApplicationMetadata(is_system);
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
Subproject commit 0846ff57b72a1bdd9fc86eee348258c0b52e0ece
|
Subproject commit 1a69955c64f72a48342abce317a75998ffac3f9d
|
|
@ -59,6 +59,12 @@ static NsApplicationControlData *g_nsAppControlData = NULL;
|
||||||
static TitleApplicationMetadata **g_systemMetadata = NULL, **g_userMetadata = NULL;
|
static TitleApplicationMetadata **g_systemMetadata = NULL, **g_userMetadata = NULL;
|
||||||
static u32 g_systemMetadataCount = 0, g_userMetadataCount = 0;
|
static u32 g_systemMetadataCount = 0, g_userMetadataCount = 0;
|
||||||
|
|
||||||
|
static TitleApplicationMetadata **g_filteredSystemMetadata = NULL, **g_filteredUserMetadata = NULL;
|
||||||
|
static u32 g_filteredSystemMetadataCount = 0, g_filteredUserMetadataCount = 0;
|
||||||
|
|
||||||
|
static TitleGameCardApplicationMetadata *g_titleGameCardApplicationMetadata = NULL;
|
||||||
|
static u32 g_titleGameCardApplicationMetadataCount = 0;
|
||||||
|
|
||||||
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;
|
||||||
|
@ -552,6 +558,9 @@ static bool titleGenerateMetadataEntriesFromNsRecords(void);
|
||||||
static TitleApplicationMetadata *titleGenerateDummySystemMetadataEntry(u64 title_id);
|
static TitleApplicationMetadata *titleGenerateDummySystemMetadataEntry(u64 title_id);
|
||||||
static bool titleRetrieveUserApplicationMetadataByTitleId(u64 title_id, TitleApplicationMetadata *out);
|
static bool titleRetrieveUserApplicationMetadataByTitleId(u64 title_id, TitleApplicationMetadata *out);
|
||||||
|
|
||||||
|
static void titleGenerateFilteredApplicationMetadataPointerArray(bool is_system);
|
||||||
|
static void titleGenerateGameCardApplicationMetadataArray(void);
|
||||||
|
|
||||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count);
|
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count);
|
||||||
|
|
||||||
NX_INLINE u64 titleGetApplicationIdByContentMetaKey(const NcmContentMetaKey *meta_key);
|
NX_INLINE u64 titleGetApplicationIdByContentMetaKey(const NcmContentMetaKey *meta_key);
|
||||||
|
@ -579,7 +588,7 @@ static int titleUserApplicationMetadataEntrySortFunction(const void *a, const vo
|
||||||
static int titleInfoEntrySortFunction(const void *a, const void *b);
|
static int titleInfoEntrySortFunction(const void *a, const void *b);
|
||||||
static int titleGameCardApplicationMetadataSortFunction(const void *a, const void *b);
|
static int titleGameCardApplicationMetadataSortFunction(const void *a, const void *b);
|
||||||
|
|
||||||
static char *titleGetPatchVersionString(TitleInfo *title_info);
|
static char *titleGetDisplayVersionString(TitleInfo *title_info);
|
||||||
|
|
||||||
bool titleInitialize(void)
|
bool titleInitialize(void)
|
||||||
{
|
{
|
||||||
|
@ -623,6 +632,12 @@ bool titleInitialize(void)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Generate filtered system application metadata pointer array. */
|
||||||
|
titleGenerateFilteredApplicationMetadataPointerArray(true);
|
||||||
|
|
||||||
|
/* Generate filtered user application metadata pointer array. */
|
||||||
|
titleGenerateFilteredApplicationMetadataPointerArray(false);
|
||||||
|
|
||||||
/* Create user-mode exit event. */
|
/* Create user-mode exit event. */
|
||||||
ueventCreate(&g_titleGameCardInfoThreadExitEvent, true);
|
ueventCreate(&g_titleGameCardInfoThreadExitEvent, true);
|
||||||
|
|
||||||
|
@ -689,158 +704,65 @@ NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id)
|
||||||
|
|
||||||
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count)
|
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count)
|
||||||
{
|
{
|
||||||
u32 app_count = 0;
|
TitleApplicationMetadata **dup_filtered_app_metadata = NULL;
|
||||||
TitleApplicationMetadata **app_metadata = NULL, **tmp_app_metadata = NULL;
|
|
||||||
|
|
||||||
SCOPED_LOCK(&g_titleMutex)
|
SCOPED_LOCK(&g_titleMutex)
|
||||||
{
|
{
|
||||||
if (!g_titleInterfaceInit || (is_system && (!g_systemMetadata || !g_systemMetadataCount)) || (!is_system && (!g_userMetadata || !g_userMetadataCount)) || !out_count)
|
TitleApplicationMetadata **filtered_app_metadata = (is_system ? g_filteredSystemMetadata : g_filteredUserMetadata);
|
||||||
|
u32 filtered_app_metadata_count = (is_system ? g_filteredSystemMetadataCount : g_filteredUserMetadataCount);
|
||||||
|
|
||||||
|
if (!g_titleInterfaceInit || !filtered_app_metadata || !filtered_app_metadata_count || !out_count)
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("Invalid parameters!");
|
LOG_MSG_ERROR("Invalid parameters!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleApplicationMetadata **cached_app_metadata = (is_system ? g_systemMetadata : g_userMetadata);
|
/* Allocate memory for the pointer array. */
|
||||||
u32 cached_app_metadata_count = (is_system ? g_systemMetadataCount : g_userMetadataCount);
|
dup_filtered_app_metadata = malloc(filtered_app_metadata_count * sizeof(TitleApplicationMetadata*));
|
||||||
bool error = false;
|
if (!dup_filtered_app_metadata)
|
||||||
|
|
||||||
for(u32 i = 0; i < cached_app_metadata_count; i++)
|
|
||||||
{
|
{
|
||||||
TitleApplicationMetadata *cur_app_metadata = cached_app_metadata[i];
|
LOG_MSG_ERROR("Failed to allocate memory for pointer array duplicate!");
|
||||||
if (!cur_app_metadata) continue;
|
break;
|
||||||
|
|
||||||
/* Skip current metadata entry if content data for this title isn't available. */
|
|
||||||
if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id)) || \
|
|
||||||
(!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue;
|
|
||||||
|
|
||||||
/* Reallocate application metadata pointer array. */
|
|
||||||
tmp_app_metadata = realloc(app_metadata, (app_count + 1) * sizeof(TitleApplicationMetadata*));
|
|
||||||
if (!tmp_app_metadata)
|
|
||||||
{
|
|
||||||
LOG_MSG_ERROR("Failed to reallocate application metadata pointer array!");
|
|
||||||
if (app_metadata) free(app_metadata);
|
|
||||||
app_metadata = NULL;
|
|
||||||
error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
app_metadata = tmp_app_metadata;
|
|
||||||
tmp_app_metadata = NULL;
|
|
||||||
|
|
||||||
/* Set current pointer and increase counter. */
|
|
||||||
app_metadata[app_count++] = cur_app_metadata;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) break;
|
/* Copy application metadata pointers. */
|
||||||
|
memcpy(dup_filtered_app_metadata, filtered_app_metadata, filtered_app_metadata_count * sizeof(TitleApplicationMetadata*));
|
||||||
|
|
||||||
/* Update output counter. */
|
/* Update output counter. */
|
||||||
*out_count = app_count;
|
*out_count = filtered_app_metadata_count;
|
||||||
|
|
||||||
if (!app_metadata || !app_count) LOG_MSG_ERROR("No content data found for %s!", is_system ? "system titles" : "user applications");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return app_metadata;
|
return dup_filtered_app_metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleGameCardApplicationMetadataEntry *titleGetGameCardApplicationMetadataEntries(u32 *out_count)
|
TitleGameCardApplicationMetadata *titleGetGameCardApplicationMetadataEntries(u32 *out_count)
|
||||||
{
|
{
|
||||||
u32 app_count = 0;
|
TitleGameCardApplicationMetadata *dup_gc_app_metadata = NULL;
|
||||||
TitleGameCardApplicationMetadataEntry *gc_app_metadata = NULL, *tmp_gc_app_metadata = NULL;
|
|
||||||
|
|
||||||
SCOPED_LOCK(&g_titleMutex)
|
SCOPED_LOCK(&g_titleMutex)
|
||||||
{
|
{
|
||||||
TitleStorage *title_storage = &(g_titleStorage[TITLE_STORAGE_INDEX(NcmStorageId_GameCard)]);
|
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || !g_titleGameCardApplicationMetadata || !g_titleGameCardApplicationMetadataCount || !out_count)
|
||||||
TitleInfo **titles = title_storage->titles;
|
|
||||||
u32 title_count = title_storage->title_count;
|
|
||||||
bool error = false;
|
|
||||||
|
|
||||||
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || !out_count || !titles || !title_count)
|
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("Invalid parameters!");
|
LOG_MSG_ERROR("Invalid parameters!");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Loop through our gamecard TitleInfo entries. */
|
/* Allocate memory for the output array. */
|
||||||
for(u32 i = 0; i < title_count; i++)
|
dup_gc_app_metadata = malloc(g_titleGameCardApplicationMetadataCount * sizeof(TitleGameCardApplicationMetadata));
|
||||||
|
if (!dup_gc_app_metadata)
|
||||||
{
|
{
|
||||||
/* Skip current entry if it's not a user application. */
|
LOG_MSG_ERROR("Failed to allocate memory for output array!");
|
||||||
TitleInfo *app_info = titles[i], *patch_info = NULL;
|
break;
|
||||||
if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue;
|
|
||||||
|
|
||||||
u32 app_version = app_info->meta_key.version;
|
|
||||||
u32 dlc_count = 0;
|
|
||||||
|
|
||||||
/* 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. */
|
|
||||||
for(u32 j = 0; j < title_count; j++)
|
|
||||||
{
|
|
||||||
if (j == i) continue;
|
|
||||||
|
|
||||||
TitleInfo *cur_title_info = titles[j];
|
|
||||||
if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_Patch || \
|
|
||||||
!titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id) || cur_title_info->meta_key.version <= app_version) continue;
|
|
||||||
|
|
||||||
patch_info = cur_title_info;
|
|
||||||
app_version = cur_title_info->meta_key.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Count DLCs available for this application in the inserted gamecard. */
|
|
||||||
for(u32 j = 0; j < title_count; j++)
|
|
||||||
{
|
|
||||||
if (j == i) continue;
|
|
||||||
|
|
||||||
TitleInfo *cur_title_info = titles[j];
|
|
||||||
if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_AddOnContent || \
|
|
||||||
!titleCheckIfAddOnContentIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id)) continue;
|
|
||||||
|
|
||||||
dlc_count++;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Reallocate application metadata pointer array. */
|
|
||||||
tmp_gc_app_metadata = realloc(gc_app_metadata, (app_count + 1) * sizeof(TitleGameCardApplicationMetadataEntry));
|
|
||||||
if (!tmp_gc_app_metadata)
|
|
||||||
{
|
|
||||||
LOG_MSG_ERROR("Failed to reallocate application metadata pointer array!");
|
|
||||||
if (gc_app_metadata) free(gc_app_metadata);
|
|
||||||
gc_app_metadata = NULL;
|
|
||||||
error = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
gc_app_metadata = tmp_gc_app_metadata;
|
|
||||||
tmp_gc_app_metadata = NULL;
|
|
||||||
|
|
||||||
/* Fill current entry and increase counter. */
|
|
||||||
tmp_gc_app_metadata = &(gc_app_metadata[app_count++]);
|
|
||||||
memset(tmp_gc_app_metadata, 0, sizeof(TitleGameCardApplicationMetadataEntry));
|
|
||||||
tmp_gc_app_metadata->app_metadata = app_info->app_metadata;
|
|
||||||
tmp_gc_app_metadata->version.value = app_version;
|
|
||||||
tmp_gc_app_metadata->dlc_count = dlc_count;
|
|
||||||
|
|
||||||
/* Try to retrieve the display version. */
|
|
||||||
char *version_str = titleGetPatchVersionString(patch_info ? patch_info : app_info);
|
|
||||||
if (version_str)
|
|
||||||
{
|
|
||||||
snprintf(tmp_gc_app_metadata->display_version, MAX_ELEMENTS(tmp_gc_app_metadata->display_version), "%s", version_str);
|
|
||||||
free(version_str);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (error) break;
|
/* Copy array data. */
|
||||||
|
memcpy(dup_gc_app_metadata, g_titleGameCardApplicationMetadata, g_titleGameCardApplicationMetadataCount * sizeof(TitleGameCardApplicationMetadata));
|
||||||
|
|
||||||
/* Update output counter. */
|
/* Update output counter. */
|
||||||
*out_count = app_count;
|
*out_count = g_titleGameCardApplicationMetadataCount;
|
||||||
|
|
||||||
if (gc_app_metadata && app_count)
|
|
||||||
{
|
|
||||||
/* Reorder title metadata entries by name. */
|
|
||||||
if (app_count > 1) qsort(gc_app_metadata, app_count, sizeof(TitleGameCardApplicationMetadataEntry), &titleGameCardApplicationMetadataSortFunction);
|
|
||||||
} else {
|
|
||||||
LOG_MSG_ERROR("No gamecard content data found for user applications!");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return gc_app_metadata;
|
return dup_gc_app_metadata;
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
|
TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id)
|
||||||
|
@ -1159,7 +1081,7 @@ char *titleGenerateFileName(TitleInfo *title_info, u8 naming_convention, u8 ille
|
||||||
snprintf(title_name, MAX_ELEMENTS(title_name), "%s ", title_info->app_metadata->lang_entry.name);
|
snprintf(title_name, MAX_ELEMENTS(title_name), "%s ", title_info->app_metadata->lang_entry.name);
|
||||||
|
|
||||||
/* Retrieve display version string if we're dealing with a Patch. */
|
/* Retrieve display version string if we're dealing with a Patch. */
|
||||||
char *version_str = (title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetPatchVersionString(title_info) : NULL);
|
char *version_str = (title_info->meta_key.type == NcmContentMetaType_Patch ? titleGetDisplayVersionString(title_info) : NULL);
|
||||||
if (version_str)
|
if (version_str)
|
||||||
{
|
{
|
||||||
title_name_len = strlen(title_name);
|
title_name_len = strlen(title_name);
|
||||||
|
@ -1192,13 +1114,9 @@ char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replac
|
||||||
|
|
||||||
SCOPED_LOCK(&g_titleMutex)
|
SCOPED_LOCK(&g_titleMutex)
|
||||||
{
|
{
|
||||||
TitleStorage *title_storage = &(g_titleStorage[TITLE_STORAGE_INDEX(NcmStorageId_GameCard)]);
|
|
||||||
TitleInfo **titles = title_storage->titles;
|
|
||||||
u32 title_count = title_storage->title_count;
|
|
||||||
|
|
||||||
GameCardHeader gc_header = {0};
|
GameCardHeader gc_header = {0};
|
||||||
size_t cur_filename_len = 0, app_name_len = 0;
|
|
||||||
char app_name[0x300] = {0};
|
char app_name[0x300] = {0};
|
||||||
|
size_t cur_filename_len = 0, app_name_len = 0;
|
||||||
bool error = false;
|
bool error = false;
|
||||||
|
|
||||||
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || naming_convention > TitleNamingConvention_IdAndVersionOnly || \
|
if (!g_titleInterfaceInit || !g_titleGameCardAvailable || naming_convention > TitleNamingConvention_IdAndVersionOnly || \
|
||||||
|
@ -1208,30 +1126,16 @@ char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replac
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Check if the gamecard title storage is empty. */
|
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. */
|
/* This is especially true for Kiosk / Quest gamecards. */
|
||||||
if (!titles || !title_count) goto fallback;
|
if (!g_titleGameCardApplicationMetadata || !g_titleGameCardApplicationMetadataCount) goto fallback;
|
||||||
|
|
||||||
for(u32 i = 0; i < title_count; i++)
|
/* Loop through our gamecard application metadata entries. */
|
||||||
|
for(u32 i = 0; i < g_titleGameCardApplicationMetadataCount; i++)
|
||||||
{
|
{
|
||||||
TitleInfo *app_info = titles[i], *patch_info = NULL;
|
const TitleGameCardApplicationMetadata *cur_gc_app_metadata = &(g_titleGameCardApplicationMetadata[i]);
|
||||||
if (!app_info || 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. */
|
|
||||||
for(u32 j = 0; j < title_count; j++)
|
|
||||||
{
|
|
||||||
if (j == i) continue;
|
|
||||||
|
|
||||||
TitleInfo *cur_title_info = titles[j];
|
|
||||||
if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_Patch || \
|
|
||||||
!titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id) || cur_title_info->meta_key.version <= app_version) continue;
|
|
||||||
|
|
||||||
patch_info = cur_title_info;
|
|
||||||
app_version = cur_title_info->meta_key.version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Generate current user application name. */
|
/* Generate current user application name. */
|
||||||
*app_name = '\0';
|
*app_name = '\0';
|
||||||
|
@ -1240,31 +1144,29 @@ char *titleGenerateGameCardFileName(u8 naming_convention, u8 illegal_char_replac
|
||||||
{
|
{
|
||||||
if (cur_filename_len) strcat(app_name, " + ");
|
if (cur_filename_len) strcat(app_name, " + ");
|
||||||
|
|
||||||
if (app_info->app_metadata && *(app_info->app_metadata->lang_entry.name))
|
if (cur_gc_app_metadata->app_metadata && cur_gc_app_metadata->app_metadata->lang_entry.name[0])
|
||||||
{
|
{
|
||||||
app_name_len = strlen(app_name);
|
app_name_len = strlen(app_name);
|
||||||
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", app_info->app_metadata->lang_entry.name);
|
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->app_metadata->lang_entry.name);
|
||||||
|
|
||||||
/* Retrieve display version string if the inserted gamecard holds a patch for the current user application. */
|
/* Append display version string if the inserted gamecard holds a patch for the current user application. */
|
||||||
char *version_str = (patch_info ? titleGetPatchVersionString(patch_info) : NULL);
|
if (cur_gc_app_metadata->has_patch && cur_gc_app_metadata->display_version[0])
|
||||||
if (version_str)
|
|
||||||
{
|
{
|
||||||
app_name_len = strlen(app_name);
|
app_name_len = strlen(app_name);
|
||||||
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", version_str);
|
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%s ", cur_gc_app_metadata->display_version);
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
app_name_len = strlen(app_name);
|
app_name_len = strlen(app_name);
|
||||||
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "[%016lX][v%u]", app_info->meta_key.id, app_version);
|
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
|
} else
|
||||||
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
|
if (naming_convention == TitleNamingConvention_IdAndVersionOnly)
|
||||||
{
|
{
|
||||||
if (cur_filename_len) strcat(app_name, "+");
|
if (cur_filename_len) strcat(app_name, "+");
|
||||||
app_name_len = strlen(app_name);
|
app_name_len = strlen(app_name);
|
||||||
snprintf(app_name + app_name_len, MAX_ELEMENTS(app_name) - app_name_len, "%016lX_v%u", app_info->meta_key.id, app_version);
|
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. */
|
/* Reallocate output buffer. */
|
||||||
|
@ -1330,6 +1232,7 @@ const char *titleGetNcmContentMetaTypeName(u8 content_meta_type)
|
||||||
|
|
||||||
NX_INLINE void titleFreeApplicationMetadata(void)
|
NX_INLINE void titleFreeApplicationMetadata(void)
|
||||||
{
|
{
|
||||||
|
/* Free cached application metadata. */
|
||||||
for(u8 i = 0; i < 2; i++)
|
for(u8 i = 0; i < 2; i++)
|
||||||
{
|
{
|
||||||
TitleApplicationMetadata **cached_app_metadata = (i == 0 ? g_systemMetadata : g_userMetadata);
|
TitleApplicationMetadata **cached_app_metadata = (i == 0 ? g_systemMetadata : g_userMetadata);
|
||||||
|
@ -1353,6 +1256,20 @@ NX_INLINE void titleFreeApplicationMetadata(void)
|
||||||
|
|
||||||
g_systemMetadata = g_userMetadata = NULL;
|
g_systemMetadata = g_userMetadata = NULL;
|
||||||
g_systemMetadataCount = g_userMetadataCount = 0;
|
g_systemMetadataCount = g_userMetadataCount = 0;
|
||||||
|
|
||||||
|
/* Free filtered application metadata. */
|
||||||
|
if (g_filteredSystemMetadata) free(g_filteredSystemMetadata);
|
||||||
|
|
||||||
|
if (g_filteredUserMetadata) free(g_filteredUserMetadata);
|
||||||
|
|
||||||
|
g_filteredSystemMetadata = g_filteredUserMetadata = NULL;
|
||||||
|
g_filteredSystemMetadataCount = g_filteredUserMetadataCount = 0;
|
||||||
|
|
||||||
|
/* Free gamecard application metadata. */
|
||||||
|
if (g_titleGameCardApplicationMetadata) free(g_titleGameCardApplicationMetadata);
|
||||||
|
|
||||||
|
g_titleGameCardApplicationMetadata = NULL;
|
||||||
|
g_titleGameCardApplicationMetadataCount = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool is_system, bool free_entries)
|
static bool titleReallocateApplicationMetadata(u32 extra_app_count, bool is_system, bool free_entries)
|
||||||
|
@ -1940,6 +1857,188 @@ static bool titleRetrieveUserApplicationMetadataByTitleId(u64 title_id, TitleApp
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void titleGenerateFilteredApplicationMetadataPointerArray(bool is_system)
|
||||||
|
{
|
||||||
|
TitleApplicationMetadata **filtered_app_metadata = NULL, **tmp_filtered_app_metadata = NULL;
|
||||||
|
u32 filtered_app_metadata_count = 0;
|
||||||
|
|
||||||
|
TitleApplicationMetadata **cached_app_metadata = (is_system ? g_systemMetadata : g_userMetadata);
|
||||||
|
u32 cached_app_metadata_count = (is_system ? g_systemMetadataCount : g_userMetadataCount);
|
||||||
|
|
||||||
|
/* Reset the right pointer and counter based on the input flag. */
|
||||||
|
if (is_system)
|
||||||
|
{
|
||||||
|
if (g_filteredSystemMetadata)
|
||||||
|
{
|
||||||
|
free(g_filteredSystemMetadata);
|
||||||
|
g_filteredSystemMetadata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_filteredSystemMetadataCount = 0;
|
||||||
|
} else {
|
||||||
|
if (g_filteredUserMetadata)
|
||||||
|
{
|
||||||
|
free(g_filteredUserMetadata);
|
||||||
|
g_filteredUserMetadata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_filteredUserMetadataCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Make sure we actually have cached application metadata entries we can work with. */
|
||||||
|
if (!cached_app_metadata || !cached_app_metadata_count)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("Cached %s application metadata array is empty!", is_system ? "system" : "user");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop through our cached application metadata entries. */
|
||||||
|
for(u32 i = 0; i < cached_app_metadata_count; i++)
|
||||||
|
{
|
||||||
|
TitleApplicationMetadata *cur_app_metadata = cached_app_metadata[i];
|
||||||
|
if (!cur_app_metadata) continue;
|
||||||
|
|
||||||
|
/* Skip current metadata entry if content data for this title isn't available. */
|
||||||
|
if ((is_system && !_titleGetInfoFromStorageByTitleId(NcmStorageId_BuiltInSystem, cur_app_metadata->title_id)) || \
|
||||||
|
(!is_system && !titleIsUserApplicationContentAvailable(cur_app_metadata->title_id))) continue;
|
||||||
|
|
||||||
|
/* Reallocate filtered application metadata pointer array. */
|
||||||
|
tmp_filtered_app_metadata = realloc(filtered_app_metadata, (filtered_app_metadata_count + 1) * sizeof(TitleApplicationMetadata*));
|
||||||
|
if (!tmp_filtered_app_metadata)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("Failed to reallocate filtered application metadata pointer array!");
|
||||||
|
if (filtered_app_metadata) free(filtered_app_metadata);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
filtered_app_metadata = tmp_filtered_app_metadata;
|
||||||
|
tmp_filtered_app_metadata = NULL;
|
||||||
|
|
||||||
|
/* Set current pointer and increase counter. */
|
||||||
|
filtered_app_metadata[filtered_app_metadata_count++] = cur_app_metadata;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!filtered_app_metadata || !filtered_app_metadata_count)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("No content data found for %s!", is_system ? "system titles" : "user applications");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update the right pointer and counter based on the input flag. */
|
||||||
|
if (is_system)
|
||||||
|
{
|
||||||
|
g_filteredSystemMetadata = filtered_app_metadata;
|
||||||
|
g_filteredSystemMetadataCount = filtered_app_metadata_count;
|
||||||
|
} else {
|
||||||
|
g_filteredUserMetadata = filtered_app_metadata;
|
||||||
|
g_filteredUserMetadataCount = filtered_app_metadata_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void titleGenerateGameCardApplicationMetadataArray(void)
|
||||||
|
{
|
||||||
|
TitleStorage *title_storage = &(g_titleStorage[TITLE_STORAGE_INDEX(NcmStorageId_GameCard)]);
|
||||||
|
TitleInfo **titles = title_storage->titles;
|
||||||
|
u32 title_count = title_storage->title_count;
|
||||||
|
TitleGameCardApplicationMetadata *tmp_gc_app_metadata = NULL;
|
||||||
|
|
||||||
|
/* Free gamecard application metadata array. */
|
||||||
|
if (g_titleGameCardApplicationMetadata)
|
||||||
|
{
|
||||||
|
free(g_titleGameCardApplicationMetadata);
|
||||||
|
g_titleGameCardApplicationMetadata = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_titleGameCardApplicationMetadataCount = 0;
|
||||||
|
|
||||||
|
/* Make sure we actually have gamecard TitleInfo entries we can work with. */
|
||||||
|
if (!titles || !title_count)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("No gamecard TitleInfo entries available!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Loop through our gamecard TitleInfo entries. */
|
||||||
|
LOG_MSG_DEBUG("Retrieving gamecard application metadata (%u title[s])...", title_count);
|
||||||
|
|
||||||
|
for(u32 i = 0; i < title_count; i++)
|
||||||
|
{
|
||||||
|
/* Skip current entry if it's not a user application. */
|
||||||
|
TitleInfo *app_info = titles[i], *patch_info = NULL;
|
||||||
|
if (!app_info || app_info->meta_key.type != NcmContentMetaType_Application) continue;
|
||||||
|
|
||||||
|
u32 app_version = app_info->meta_key.version;
|
||||||
|
u32 dlc_count = 0;
|
||||||
|
|
||||||
|
/* 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. */
|
||||||
|
for(u32 j = 0; j < title_count; j++)
|
||||||
|
{
|
||||||
|
if (j == i) continue;
|
||||||
|
|
||||||
|
TitleInfo *cur_title_info = titles[j];
|
||||||
|
if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_Patch || \
|
||||||
|
!titleCheckIfPatchIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id) || cur_title_info->meta_key.version <= app_version) continue;
|
||||||
|
|
||||||
|
patch_info = cur_title_info;
|
||||||
|
app_version = cur_title_info->meta_key.version;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Count DLCs available for this application in the inserted gamecard. */
|
||||||
|
for(u32 j = 0; j < title_count; j++)
|
||||||
|
{
|
||||||
|
if (j == i) continue;
|
||||||
|
|
||||||
|
TitleInfo *cur_title_info = titles[j];
|
||||||
|
if (!cur_title_info || cur_title_info->meta_key.type != NcmContentMetaType_AddOnContent || \
|
||||||
|
!titleCheckIfAddOnContentIdBelongsToApplicationId(app_info->meta_key.id, cur_title_info->meta_key.id)) continue;
|
||||||
|
|
||||||
|
dlc_count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Reallocate application metadata pointer array. */
|
||||||
|
tmp_gc_app_metadata = realloc(g_titleGameCardApplicationMetadata, (g_titleGameCardApplicationMetadataCount + 1) * sizeof(TitleGameCardApplicationMetadata));
|
||||||
|
if (!tmp_gc_app_metadata)
|
||||||
|
{
|
||||||
|
LOG_MSG_ERROR("Failed to reallocate gamecard application metadata array!");
|
||||||
|
|
||||||
|
if (g_titleGameCardApplicationMetadata) free(g_titleGameCardApplicationMetadata);
|
||||||
|
g_titleGameCardApplicationMetadata = NULL;
|
||||||
|
g_titleGameCardApplicationMetadataCount = 0;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_titleGameCardApplicationMetadata = tmp_gc_app_metadata;
|
||||||
|
tmp_gc_app_metadata = NULL;
|
||||||
|
|
||||||
|
/* Fill current entry and increase counter. */
|
||||||
|
tmp_gc_app_metadata = &(g_titleGameCardApplicationMetadata[g_titleGameCardApplicationMetadataCount++]);
|
||||||
|
memset(tmp_gc_app_metadata, 0, sizeof(TitleGameCardApplicationMetadata));
|
||||||
|
tmp_gc_app_metadata->app_metadata = app_info->app_metadata;
|
||||||
|
tmp_gc_app_metadata->has_patch = (patch_info != NULL);
|
||||||
|
tmp_gc_app_metadata->version.value = app_version;
|
||||||
|
tmp_gc_app_metadata->dlc_count = dlc_count;
|
||||||
|
|
||||||
|
/* Try to retrieve the display version. */
|
||||||
|
char *version_str = titleGetDisplayVersionString(patch_info ? patch_info : app_info);
|
||||||
|
if (version_str)
|
||||||
|
{
|
||||||
|
snprintf(tmp_gc_app_metadata->display_version, MAX_ELEMENTS(tmp_gc_app_metadata->display_version), "%s", version_str);
|
||||||
|
free(version_str);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (g_titleGameCardApplicationMetadata && g_titleGameCardApplicationMetadataCount)
|
||||||
|
{
|
||||||
|
/* Reorder title metadata entries by name. */
|
||||||
|
if (g_titleGameCardApplicationMetadataCount > 1) qsort(g_titleGameCardApplicationMetadata, g_titleGameCardApplicationMetadataCount, sizeof(TitleGameCardApplicationMetadata),
|
||||||
|
&titleGameCardApplicationMetadataSortFunction);
|
||||||
|
} else {
|
||||||
|
LOG_MSG_ERROR("No gamecard content data found for user applications!");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count)
|
NX_INLINE TitleApplicationMetadata *titleFindApplicationMetadataByTitleId(u64 title_id, bool is_system, u32 extra_app_count)
|
||||||
{
|
{
|
||||||
if (!title_id || (is_system && (!g_systemMetadata || !g_systemMetadataCount)) || (!is_system && (!g_userMetadata || !g_userMetadataCount))) return NULL;
|
if (!title_id || (is_system && (!g_systemMetadata || !g_systemMetadataCount)) || (!is_system && (!g_userMetadata || !g_userMetadataCount))) return NULL;
|
||||||
|
@ -2388,7 +2487,19 @@ static void titleGameCardInfoThreadFunc(void *arg)
|
||||||
if (idx == 1) break;
|
if (idx == 1) break;
|
||||||
|
|
||||||
/* Update gamecard title info. */
|
/* Update gamecard title info. */
|
||||||
SCOPED_LOCK(&g_titleMutex) g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo();
|
SCOPED_LOCK(&g_titleMutex)
|
||||||
|
{
|
||||||
|
g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo();
|
||||||
|
|
||||||
|
if (g_titleGameCardInfoUpdated)
|
||||||
|
{
|
||||||
|
/* Generate filtered user application metadata pointer array. */
|
||||||
|
titleGenerateFilteredApplicationMetadataPointerArray(false);
|
||||||
|
|
||||||
|
/* Generate gamecard application metadata array. */
|
||||||
|
titleGenerateGameCardApplicationMetadataArray();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Update gamecard flags. */
|
/* Update gamecard flags. */
|
||||||
|
@ -2793,13 +2904,13 @@ static int titleInfoEntrySortFunction(const void *a, const void *b)
|
||||||
|
|
||||||
static int titleGameCardApplicationMetadataSortFunction(const void *a, const void *b)
|
static int titleGameCardApplicationMetadataSortFunction(const void *a, const void *b)
|
||||||
{
|
{
|
||||||
const TitleGameCardApplicationMetadataEntry *gc_app_metadata_1 = (const TitleGameCardApplicationMetadataEntry*)a;
|
const TitleGameCardApplicationMetadata *gc_app_metadata_1 = (const TitleGameCardApplicationMetadata*)a;
|
||||||
const TitleGameCardApplicationMetadataEntry *gc_app_metadata_2 = (const TitleGameCardApplicationMetadataEntry*)b;
|
const TitleGameCardApplicationMetadata *gc_app_metadata_2 = (const TitleGameCardApplicationMetadata*)b;
|
||||||
|
|
||||||
return strcasecmp(gc_app_metadata_1->app_metadata->lang_entry.name, gc_app_metadata_2->app_metadata->lang_entry.name);
|
return strcasecmp(gc_app_metadata_1->app_metadata->lang_entry.name, gc_app_metadata_2->app_metadata->lang_entry.name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static char *titleGetPatchVersionString(TitleInfo *title_info)
|
static char *titleGetDisplayVersionString(TitleInfo *title_info)
|
||||||
{
|
{
|
||||||
NcmContentInfo *nacp_content = NULL;
|
NcmContentInfo *nacp_content = NULL;
|
||||||
u8 storage_id = NcmStorageId_None, hfs_partition_type = HashFileSystemPartitionType_None;
|
u8 storage_id = NcmStorageId_None, hfs_partition_type = HashFileSystemPartitionType_None;
|
||||||
|
@ -2807,12 +2918,17 @@ static char *titleGetPatchVersionString(TitleInfo *title_info)
|
||||||
NacpContext nacp_ctx = {0};
|
NacpContext nacp_ctx = {0};
|
||||||
char display_version[0x11] = {0}, *str = NULL;
|
char display_version[0x11] = {0}, *str = NULL;
|
||||||
|
|
||||||
if (!title_info || title_info->meta_key.type != NcmContentMetaType_Patch || !(nacp_content = titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Control, 0)))
|
if (!title_info || (title_info->meta_key.type != NcmContentMetaType_Application && title_info->meta_key.type != NcmContentMetaType_Patch) || \
|
||||||
|
!(nacp_content = titleGetContentInfoByTypeAndIdOffset(title_info, NcmContentType_Control, 0)))
|
||||||
{
|
{
|
||||||
LOG_MSG_ERROR("Invalid parameters!");
|
LOG_MSG_ERROR("Invalid parameters!");
|
||||||
goto end;
|
goto end;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LOG_MSG_DEBUG("Retrieving display version string for %s \"%s\" (%016lX) in %s...", titleGetNcmContentMetaTypeName(title_info->meta_key.type), \
|
||||||
|
title_info->app_metadata->lang_entry.name, title_info->meta_key.id, \
|
||||||
|
titleGetNcmStorageIdName(title_info->storage_id));
|
||||||
|
|
||||||
/* Update parameters. */
|
/* Update parameters. */
|
||||||
storage_id = title_info->storage_id;
|
storage_id = title_info->storage_id;
|
||||||
if (storage_id == NcmStorageId_GameCard) hfs_partition_type = HashFileSystemPartitionType_Secure;
|
if (storage_id == NcmStorageId_GameCard) hfs_partition_type = HashFileSystemPartitionType_Secure;
|
||||||
|
|
|
@ -28,9 +28,10 @@ namespace nxdt::tasks
|
||||||
GameCardStatusTask::GameCardStatusTask() : brls::RepeatingTask(REPEATING_TASK_INTERVAL)
|
GameCardStatusTask::GameCardStatusTask() : brls::RepeatingTask(REPEATING_TASK_INTERVAL)
|
||||||
{
|
{
|
||||||
brls::RepeatingTask::start();
|
brls::RepeatingTask::start();
|
||||||
LOG_MSG_DEBUG("Gamecard task started.");
|
|
||||||
|
|
||||||
this->first_notification = (gamecardGetStatus() >= GameCardStatus_Processing);
|
this->first_notification = (gamecardGetStatus() != GameCardStatus_NotInserted);
|
||||||
|
|
||||||
|
LOG_MSG_DEBUG("Gamecard task started with first_notification = %u.", this->first_notification);
|
||||||
}
|
}
|
||||||
|
|
||||||
GameCardStatusTask::~GameCardStatusTask()
|
GameCardStatusTask::~GameCardStatusTask()
|
||||||
|
|
|
@ -34,11 +34,6 @@ namespace nxdt::tasks
|
||||||
LOG_MSG_DEBUG("Status info task stopped.");
|
LOG_MSG_DEBUG("Status info task stopped.");
|
||||||
}
|
}
|
||||||
|
|
||||||
bool StatusInfoTask::IsInternetConnectionAvailable(void)
|
|
||||||
{
|
|
||||||
return this->status_info_data.connected;
|
|
||||||
}
|
|
||||||
|
|
||||||
void StatusInfoTask::run(retro_time_t current_time)
|
void StatusInfoTask::run(retro_time_t current_time)
|
||||||
{
|
{
|
||||||
brls::RepeatingTask::run(current_time);
|
brls::RepeatingTask::run(current_time);
|
||||||
|
|
|
@ -27,6 +27,8 @@ namespace nxdt::tasks
|
||||||
{
|
{
|
||||||
TitleMetadataTask::TitleMetadataTask() : brls::RepeatingTask(REPEATING_TASK_INTERVAL)
|
TitleMetadataTask::TitleMetadataTask() : brls::RepeatingTask(REPEATING_TASK_INTERVAL)
|
||||||
{
|
{
|
||||||
|
LOG_MSG_DEBUG("Title metadata task started.");
|
||||||
|
|
||||||
/* Get system metadata entries. */
|
/* Get system metadata entries. */
|
||||||
this->PopulateApplicationMetadataVector(true);
|
this->PopulateApplicationMetadataVector(true);
|
||||||
|
|
||||||
|
@ -35,7 +37,6 @@ namespace nxdt::tasks
|
||||||
|
|
||||||
/* Start task. */
|
/* Start task. */
|
||||||
brls::RepeatingTask::start();
|
brls::RepeatingTask::start();
|
||||||
LOG_MSG_DEBUG("Title metadata task started.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TitleMetadataTask::~TitleMetadataTask()
|
TitleMetadataTask::~TitleMetadataTask()
|
||||||
|
@ -53,20 +54,15 @@ namespace nxdt::tasks
|
||||||
|
|
||||||
if (titleIsGameCardInfoUpdated())
|
if (titleIsGameCardInfoUpdated())
|
||||||
{
|
{
|
||||||
LOG_MSG_DEBUG("Title info updated.");
|
|
||||||
//brls::Application::notify("tasks/notifications/user_titles"_i18n);
|
|
||||||
|
|
||||||
/* Update user metadata vector. */
|
/* Update user metadata vector. */
|
||||||
this->PopulateApplicationMetadataVector(false);
|
this->PopulateApplicationMetadataVector(false);
|
||||||
|
|
||||||
/* Fire task event. */
|
/* Fire task event. */
|
||||||
this->user_title_event.fire(this->user_metadata);
|
this->user_title_event.fire(this->user_metadata);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const TitleApplicationMetadataVector& TitleMetadataTask::GetApplicationMetadata(bool is_system)
|
//brls::Application::notify("tasks/notifications/user_titles"_i18n);
|
||||||
{
|
LOG_MSG_DEBUG("Title info updated.");
|
||||||
return (is_system ? this->system_metadata : this->user_metadata);
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void TitleMetadataTask::PopulateApplicationMetadataVector(bool is_system)
|
void TitleMetadataTask::PopulateApplicationMetadataVector(bool is_system)
|
||||||
|
|
|
@ -59,11 +59,6 @@ namespace nxdt::tasks
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const UmsDeviceVector& UmsTask::GetUmsDevices(void)
|
|
||||||
{
|
|
||||||
return this->ums_devices_vector;
|
|
||||||
}
|
|
||||||
|
|
||||||
void UmsTask::PopulateUmsDeviceVector(void)
|
void UmsTask::PopulateUmsDeviceVector(void)
|
||||||
{
|
{
|
||||||
/* Clear UMS device vector. */
|
/* Clear UMS device vector. */
|
||||||
|
|
|
@ -53,9 +53,4 @@ namespace nxdt::tasks
|
||||||
this->usb_host_event.fire(this->cur_usb_host_speed);
|
this->usb_host_event.fire(this->cur_usb_host_speed);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const UsbHostSpeed& UsbHostTask::GetUsbHostSpeed(void)
|
|
||||||
{
|
|
||||||
return this->cur_usb_host_speed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ namespace nxdt::views
|
||||||
this->list->setMarginBottom(20);
|
this->list->setMarginBottom(20);
|
||||||
|
|
||||||
/* Subscribe to the gamecard status event. */
|
/* Subscribe to the gamecard status event. */
|
||||||
this->gc_status_task_sub = this->root_view->RegisterGameCardStatusTaskListener([this](GameCardStatus gc_status) {
|
this->gc_status_task_sub = this->root_view->RegisterGameCardStatusTaskListener([this](const GameCardStatus& gc_status) {
|
||||||
/* Process gamecard status. */
|
/* Process gamecard status. */
|
||||||
this->ProcessGameCardStatus(gc_status);
|
this->ProcessGameCardStatus(gc_status);
|
||||||
});
|
});
|
||||||
|
@ -58,8 +58,13 @@ namespace nxdt::views
|
||||||
this->root_view->UnregisterGameCardStatusTaskListener(this->gc_status_task_sub);
|
this->root_view->UnregisterGameCardStatusTaskListener(this->gc_status_task_sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameCardTab::ProcessGameCardStatus(GameCardStatus gc_status)
|
void GameCardTab::ProcessGameCardStatus(const GameCardStatus& gc_status)
|
||||||
{
|
{
|
||||||
|
LOG_MSG_DEBUG("Processing gamecard status: %u.", gc_status);
|
||||||
|
|
||||||
|
/* Block user inputs. */
|
||||||
|
brls::Application::blockInputs();
|
||||||
|
|
||||||
/* Switch to the error layer if gamecard info hasn't been loaded. */
|
/* Switch to the error layer if gamecard info hasn't been loaded. */
|
||||||
if (gc_status < GameCardStatus_InsertedAndInfoLoaded) this->SwitchLayerView(true);
|
if (gc_status < GameCardStatus_InsertedAndInfoLoaded) this->SwitchLayerView(true);
|
||||||
|
|
||||||
|
@ -90,6 +95,9 @@ namespace nxdt::views
|
||||||
|
|
||||||
/* Update internal gamecard status. */
|
/* Update internal gamecard status. */
|
||||||
this->gc_status = gc_status;
|
this->gc_status = gc_status;
|
||||||
|
|
||||||
|
/* Unlock user inputs. */
|
||||||
|
brls::Application::unblockInputs();
|
||||||
}
|
}
|
||||||
|
|
||||||
void GameCardTab::PopulateList(void)
|
void GameCardTab::PopulateList(void)
|
||||||
|
@ -155,7 +163,7 @@ namespace nxdt::views
|
||||||
|
|
||||||
void GameCardTab::AddApplicationMetadataItems(void)
|
void GameCardTab::AddApplicationMetadataItems(void)
|
||||||
{
|
{
|
||||||
TitleGameCardApplicationMetadataEntry *gc_app_metadata = nullptr;
|
TitleGameCardApplicationMetadata *gc_app_metadata = nullptr;
|
||||||
u32 gc_app_metadata_count = 0;
|
u32 gc_app_metadata_count = 0;
|
||||||
|
|
||||||
/* Retrieve gamecard application metadata. */
|
/* Retrieve gamecard application metadata. */
|
||||||
|
@ -176,7 +184,7 @@ namespace nxdt::views
|
||||||
/* Add gamecard application metadata items. */
|
/* Add gamecard application metadata items. */
|
||||||
for(u32 i = 0; i < gc_app_metadata_count; i++)
|
for(u32 i = 0; i < gc_app_metadata_count; i++)
|
||||||
{
|
{
|
||||||
TitleGameCardApplicationMetadataEntry *cur_gc_app_metadata = &(gc_app_metadata[i]);
|
TitleGameCardApplicationMetadata *cur_gc_app_metadata = &(gc_app_metadata[i]);
|
||||||
|
|
||||||
/* Create item. */
|
/* Create item. */
|
||||||
TitlesTabItem *title = new TitlesTabItem(cur_gc_app_metadata->app_metadata, false, false);
|
TitlesTabItem *title = new TitlesTabItem(cur_gc_app_metadata->app_metadata, false, false);
|
||||||
|
|
|
@ -102,6 +102,9 @@ namespace nxdt::views
|
||||||
|
|
||||||
void TitlesTab::PopulateList(const nxdt::tasks::TitleApplicationMetadataVector& app_metadata)
|
void TitlesTab::PopulateList(const nxdt::tasks::TitleApplicationMetadataVector& app_metadata)
|
||||||
{
|
{
|
||||||
|
/* Block user inputs. */
|
||||||
|
brls::Application::blockInputs();
|
||||||
|
|
||||||
/* Populate variables. */
|
/* Populate variables. */
|
||||||
size_t app_metadata_count = app_metadata.size();
|
size_t app_metadata_count = app_metadata.size();
|
||||||
bool update_focused_view = this->IsListItemFocused();
|
bool update_focused_view = this->IsListItemFocused();
|
||||||
|
@ -114,8 +117,12 @@ namespace nxdt::views
|
||||||
this->list->clear();
|
this->list->clear();
|
||||||
this->list->invalidate(true);
|
this->list->invalidate(true);
|
||||||
|
|
||||||
/* Return immediately if we have no user application metadata. */
|
/* Return immediately if we have no application metadata. */
|
||||||
if (!app_metadata_count) return;
|
if (!app_metadata_count)
|
||||||
|
{
|
||||||
|
brls::Application::unblockInputs();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
/* Populate list. */
|
/* Populate list. */
|
||||||
for(const TitleApplicationMetadata *cur_app_metadata : app_metadata)
|
for(const TitleApplicationMetadata *cur_app_metadata : app_metadata)
|
||||||
|
@ -126,14 +133,14 @@ namespace nxdt::views
|
||||||
/* Register click event. */
|
/* Register click event. */
|
||||||
item->getClickEvent()->subscribe([](brls::View *view) {
|
item->getClickEvent()->subscribe([](brls::View *view) {
|
||||||
TitlesTabItem *item = static_cast<TitlesTabItem*>(view);
|
TitlesTabItem *item = static_cast<TitlesTabItem*>(view);
|
||||||
const TitleApplicationMetadata *app_metadata = item->GetApplicationMetadata();
|
const TitleApplicationMetadata *item_app_metadata = item->GetApplicationMetadata();
|
||||||
bool is_system = item->IsSystemTitle();
|
bool is_system = item->IsSystemTitle();
|
||||||
|
|
||||||
/* Create popup. */
|
/* Create popup. */
|
||||||
TitlesTabPopup *popup = nullptr;
|
TitlesTabPopup *popup = nullptr;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
popup = new TitlesTabPopup(app_metadata, is_system);
|
popup = new TitlesTabPopup(item_app_metadata, is_system);
|
||||||
} catch(const std::string& msg) {
|
} catch(const std::string& msg) {
|
||||||
LOG_MSG_DEBUG("%s", msg.c_str());
|
LOG_MSG_DEBUG("%s", msg.c_str());
|
||||||
if (popup) delete popup;
|
if (popup) delete popup;
|
||||||
|
@ -141,14 +148,14 @@ namespace nxdt::views
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Display popup. */
|
/* Display popup. */
|
||||||
std::string name = std::string(app_metadata->lang_entry.name);
|
std::string name = std::string(item_app_metadata->lang_entry.name);
|
||||||
std::string tid = fmt::format("{:016X}", app_metadata->title_id);
|
std::string tid = fmt::format("{:016X}", item_app_metadata->title_id);
|
||||||
std::string sub_left = (!is_system ? std::string(app_metadata->lang_entry.author) : tid);
|
std::string sub_left = (!is_system ? std::string(item_app_metadata->lang_entry.author) : tid);
|
||||||
std::string sub_right = (!is_system ? tid : "");
|
std::string sub_right = (!is_system ? tid : "");
|
||||||
|
|
||||||
if (app_metadata->icon && app_metadata->icon_size)
|
if (item_app_metadata->icon && item_app_metadata->icon_size)
|
||||||
{
|
{
|
||||||
brls::PopupFrame::open(name, app_metadata->icon, app_metadata->icon_size, popup, sub_left, sub_right);
|
brls::PopupFrame::open(name, item_app_metadata->icon, item_app_metadata->icon_size, popup, sub_left, sub_right);
|
||||||
} else {
|
} else {
|
||||||
brls::PopupFrame::open(name, popup, sub_left, sub_right);
|
brls::PopupFrame::open(name, popup, sub_left, sub_right);
|
||||||
}
|
}
|
||||||
|
@ -164,5 +171,8 @@ namespace nxdt::views
|
||||||
/* Switch to the list. */
|
/* Switch to the list. */
|
||||||
this->list->invalidate(true);
|
this->list->invalidate(true);
|
||||||
this->SwitchLayerView(false, update_focused_view, focus_stack_index < 0);
|
this->SwitchLayerView(false, update_focused_view, focus_stack_index < 0);
|
||||||
|
|
||||||
|
/* Unblock user inputs. */
|
||||||
|
brls::Application::unblockInputs();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue