Threaded gamecard title info/metadata retrieval.

This commit is contained in:
Pablo Curiel 2020-07-30 17:43:50 -04:00
parent cd8afd2cd8
commit bb8cba1eaa
4 changed files with 161 additions and 53 deletions

View file

@ -213,10 +213,13 @@ int main(int argc, char *argv[])
goto out; goto out;
} }
u32 app_count = 0, selected_idx = 0; u32 app_count = 0;
TitleApplicationMetadata **app_metadata = NULL; TitleApplicationMetadata **app_metadata = NULL;
TitleUserApplicationData user_app_data = {0}; TitleUserApplicationData user_app_data = {0};
u32 selected_idx = 0, page_size = 30, cur_page = 0;
bool exit_prompt = true;
u8 *buf = NULL; u8 *buf = NULL;
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL; NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
@ -268,21 +271,27 @@ int main(int argc, char *argv[])
while(true) while(true)
{ {
consoleClear(); consoleClear();
printf("select a base title with an available update.\nthe updated romfs will be dumped via usb.\npress b to cancel.\n\n"); printf("select a base title with an available update.\nthe updated romfs will be dumped via usb.\npress b to exit.\n\n");
for(u32 i = cur_page; i < app_count; i++)
{
if (i >= (cur_page + page_size)) break;
printf("%s%s (%016lX)\n", i == selected_idx ? " -> " : " ", app_metadata[i]->lang_entry.name, app_metadata[i]->title_id);
}
printf("\n");
for(u32 i = 0; i < app_count; i++) printf("%s%s (%016lX)\n", i == selected_idx ? " -> " : " ", app_metadata[i]->lang_entry.name, app_metadata[i]->title_id);
consoleUpdate(NULL); consoleUpdate(NULL);
u64 btn = 0; u64 btn_down = 0, btn_held = 0;
while(true) while(true)
{ {
hidScanInput(); hidScanInput();
btn_down = utilsHidKeysAllDown();
btn_held = utilsHidKeysAllHeld();
if (btn_down || btn_held) break;
btn = utilsHidKeysAllDown(); if (titleIsGameCardInfoUpdated())
if (btn) break;
if (titleRefreshGameCardTitleInfo())
{ {
free(app_metadata); free(app_metadata);
@ -293,13 +302,12 @@ int main(int argc, char *argv[])
goto out2; goto out2;
} }
selected_idx = 0; selected_idx = cur_page = 0;
break; break;
} }
} }
if (btn & KEY_A) if (btn_down & KEY_A)
{ {
if (!titleGetUserApplicationData(app_metadata[selected_idx]->title_id, &user_app_data) || !user_app_data.app_info || !user_app_data.patch_info) if (!titleGetUserApplicationData(app_metadata[selected_idx]->title_id, &user_app_data) || !user_app_data.app_info || !user_app_data.patch_info)
{ {
@ -310,29 +318,50 @@ int main(int argc, char *argv[])
break; break;
} else } else
if (btn & KEY_DOWN) if ((btn_down & KEY_DDOWN) || (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN)))
{
if ((selected_idx + 1) < app_count)
{ {
selected_idx++; selected_idx++;
if (selected_idx >= app_count)
{
if (btn_down & KEY_DDOWN)
{
selected_idx = cur_page = 0;
} else {
selected_idx = (app_count - 1);
}
} else
if (selected_idx >= (cur_page + page_size))
{
cur_page += page_size;
}
} else
if ((btn_down & KEY_DUP) || (btn_held & (KEY_LSTICK_UP | KEY_RSTICK_UP)))
{
selected_idx--;
if (selected_idx == UINT32_MAX)
{
if (btn_down & KEY_DUP)
{
selected_idx = (app_count - 1);
cur_page = (app_count - (app_count % page_size));
} else { } else {
selected_idx = 0; selected_idx = 0;
} }
} else } else
if (btn & KEY_UP) if (selected_idx < cur_page)
{ {
if (selected_idx == 0) cur_page -= page_size;
{
selected_idx = (app_count - 1);
} else {
selected_idx--;
} }
} else } else
if (btn & KEY_B) if (btn_down & KEY_B)
{ {
consolePrint("\nprocess cancelled.\n"); exit_prompt = false;
goto out2; goto out2;
} }
if (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN | KEY_LSTICK_UP | KEY_RSTICK_UP)) svcSleepThread(50000000); // 50 ms
} }
consoleClear(); consoleClear();
@ -469,8 +498,11 @@ int main(int argc, char *argv[])
consolePrint("process completed in %lu seconds\n", start); consolePrint("process completed in %lu seconds\n", start);
out2: out2:
if (exit_prompt)
{
consolePrint("press any button to exit\n"); consolePrint("press any button to exit\n");
utilsWaitForButtonPress(KEY_NONE); utilsWaitForButtonPress(KEY_NONE);
}
bktrFreeContext(&bktr_ctx); bktrFreeContext(&bktr_ctx);

View file

@ -34,7 +34,10 @@ typedef struct {
/* Global variables. */ /* Global variables. */
static Mutex g_titleMutex = 0; static Mutex g_titleMutex = 0;
static bool g_titleInterfaceInit = false, g_titleGameCardAvailable = false; static thrd_t g_titleGameCardInfoThread;
static UEvent g_titleGameCardInfoThreadExitEvent = {0}, *g_titleGameCardStatusChangeUserEvent = NULL;
static bool g_titleInterfaceInit = false, g_titleGameCardInfoThreadCreated = false, g_titleGameCardAvailable = false, g_titleGameCardInfoUpdated = false;
static NsApplicationControlData *g_nsAppControlData = NULL; static NsApplicationControlData *g_nsAppControlData = NULL;
@ -373,11 +376,15 @@ static void titleCloseNcmStorages(void);
static bool titleOpenNcmDatabaseAndStorageFromGameCard(void); static bool titleOpenNcmDatabaseAndStorageFromGameCard(void);
static void titleCloseNcmDatabaseAndStorageFromGameCard(void); static void titleCloseNcmDatabaseAndStorageFromGameCard(void);
static bool titleLoadTitleInfo(void); static bool titleLoadPersistentStorageTitleInfo(void);
static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id); static bool titleRetrieveContentMetaKeysFromDatabase(u8 storage_id);
static bool titleGetContentInfosFromTitle(u8 storage_id, const NcmContentMetaKey *meta_key, NcmContentInfo **out_content_infos, u32 *out_content_count); static bool titleGetContentInfosFromTitle(u8 storage_id, const NcmContentMetaKey *meta_key, NcmContentInfo **out_content_infos, u32 *out_content_count);
static bool _titleRefreshGameCardTitleInfo(bool lock); static bool titleCreateGameCardInfoThread(void);
static void titleDestroyGameCardInfoThread(void);
static int titleGameCardInfoThreadFunc(void *arg);
static bool titleRefreshGameCardTitleInfo(void);
static void titleRemoveGameCardTitleInfoEntries(void); static void titleRemoveGameCardTitleInfoEntries(void);
static bool titleIsUserApplicationContentAvailable(u64 app_id); static bool titleIsUserApplicationContentAvailable(u64 app_id);
@ -433,19 +440,25 @@ bool titleInitialize(void)
} }
/* Load title info by retrieving content meta keys from available eMMC System, eMMC User and SD card titles. */ /* Load title info by retrieving content meta keys from available eMMC System, eMMC User and SD card titles. */
if (!titleLoadTitleInfo()) if (!titleLoadPersistentStorageTitleInfo())
{ {
LOGFILE("Failed to load title info!"); LOGFILE("Failed to load persistent storage title info!");
goto end; goto end;
} }
/* Initial gamecard title info retrieval. */ /* Create usermode exit event. */
_titleRefreshGameCardTitleInfo(false); ueventCreate(&g_titleGameCardInfoThreadExitEvent, true);
/* Retrieve gamecard status change user event. */
g_titleGameCardStatusChangeUserEvent = gamecardGetStatusChangeUserEvent();
if (!g_titleGameCardStatusChangeUserEvent)
{
LOGFILE("Failed to retrieve gamecard status change user event!");
goto end;
}
/* Create gamecard title info thread. */
if (!(g_titleGameCardInfoThreadCreated = titleCreateGameCardInfoThread())) goto end;
/* /*
@ -554,10 +567,17 @@ void titleExit(void)
{ {
mutexLock(&g_titleMutex); mutexLock(&g_titleMutex);
/* Destroy gamecard detection thread. */
if (g_titleGameCardInfoThreadCreated)
{
titleDestroyGameCardInfoThread();
g_titleGameCardInfoThreadCreated = false;
}
/* Free title info. */ /* Free title info. */
titleFreeTitleInfo(); titleFreeTitleInfo();
/* Close gamecard ncm database and storage. */ /* Close gamecard ncm database and storage (if needed). */
titleCloseNcmDatabaseAndStorageFromGameCard(); titleCloseNcmDatabaseAndStorageFromGameCard();
/* Close eMMC System, eMMC User and SD card ncm storages. */ /* Close eMMC System, eMMC User and SD card ncm storages. */
@ -627,11 +647,6 @@ NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id)
return ncm_storage; return ncm_storage;
} }
bool titleRefreshGameCardTitleInfo(void)
{
return _titleRefreshGameCardTitleInfo(true);
}
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count) TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count)
{ {
mutexLock(&g_titleMutex); mutexLock(&g_titleMutex);
@ -743,6 +758,15 @@ end:
return success; return success;
} }
bool titleIsGameCardInfoUpdated(void)
{
mutexLock(&g_titleMutex);
bool ret = g_titleGameCardInfoUpdated;
if (ret) g_titleGameCardInfoUpdated = false; /* Update flag to avoid updating application metadata entries in the caller function if it's not needed. */
mutexUnlock(&g_titleMutex);
return ret;
}
const char *titleGetNcmContentTypeName(u8 content_type) const char *titleGetNcmContentTypeName(u8 content_type)
{ {
u8 idx = (content_type > NcmContentType_DeltaFragment ? (NcmContentType_DeltaFragment + 1) : content_type); u8 idx = (content_type > NcmContentType_DeltaFragment ? (NcmContentType_DeltaFragment + 1) : content_type);
@ -1118,7 +1142,7 @@ static void titleCloseNcmDatabaseAndStorageFromGameCard(void)
if (serviceIsActive(&(ncm_storage->s))) ncmContentStorageClose(ncm_storage); if (serviceIsActive(&(ncm_storage->s))) ncmContentStorageClose(ncm_storage);
} }
static bool titleLoadTitleInfo(void) static bool titleLoadPersistentStorageTitleInfo(void)
{ {
/* Return right away if title info has already been retrieved. */ /* Return right away if title info has already been retrieved. */
if (g_titleInfo || g_titleInfoCount) return true; if (g_titleInfo || g_titleInfoCount) return true;
@ -1384,10 +1408,64 @@ end:
return success; return success;
} }
static bool _titleRefreshGameCardTitleInfo(bool lock) static bool titleCreateGameCardInfoThread(void)
{ {
if (lock) mutexLock(&g_titleMutex); if (thrd_create(&g_titleGameCardInfoThread, titleGameCardInfoThreadFunc, NULL) != thrd_success)
{
LOGFILE("Failed to create gamecard title info thread!");
return false;
}
return true;
}
static void titleDestroyGameCardInfoThread(void)
{
/* Signal the exit event to terminate the gamecard title info thread. */
ueventSignal(&g_titleGameCardInfoThreadExitEvent);
/* Wait for the gamecard title info thread to exit. */
thrd_join(g_titleGameCardInfoThread, NULL);
}
static int titleGameCardInfoThreadFunc(void *arg)
{
(void)arg;
Result rc = 0;
int idx = 0;
Waiter gamecard_status_event_waiter = waiterForUEvent(g_titleGameCardStatusChangeUserEvent);
Waiter exit_event_waiter = waiterForUEvent(&g_titleGameCardInfoThreadExitEvent);
/* Initial gamecard title info retrieval. */
mutexLock(&g_titleMutex);
titleRefreshGameCardTitleInfo();
mutexUnlock(&g_titleMutex);
while(true)
{
/* Wait until an event is triggered. */
rc = waitMulti(&idx, -1, gamecard_status_event_waiter, exit_event_waiter);
if (R_FAILED(rc)) continue;
/* Exit event triggered. */
if (idx == 1) break;
/* Update gamecard title info. */
mutexLock(&g_titleMutex);
g_titleGameCardInfoUpdated = titleRefreshGameCardTitleInfo();
mutexUnlock(&g_titleMutex);
}
/* Update gamecard flags. */
g_titleGameCardAvailable = g_titleGameCardInfoUpdated = false;
return 0;
}
static bool titleRefreshGameCardTitleInfo(void)
{
TitleApplicationMetadata *tmp_app_metadata = NULL; TitleApplicationMetadata *tmp_app_metadata = NULL;
u32 orig_app_count = g_appMetadataCount, cur_app_count = g_appMetadataCount, gamecard_app_count = 0, gamecard_metadata_count = 0; u32 orig_app_count = g_appMetadataCount, cur_app_count = g_appMetadataCount, gamecard_app_count = 0, gamecard_metadata_count = 0;
bool status = false, success = false, cleanup = true; bool status = false, success = false, cleanup = true;
@ -1487,7 +1565,7 @@ end:
/* Update gamecard status. */ /* Update gamecard status. */
g_titleGameCardAvailable = status; g_titleGameCardAvailable = status;
/* Decrease application metadata buffer size if needed. */ /* Decrease application metadata buffer size (if needed). */
if ((success && g_appMetadataCount < cur_app_count) || (!success && g_appMetadataCount > orig_app_count)) if ((success && g_appMetadataCount < cur_app_count) || (!success && g_appMetadataCount > orig_app_count))
{ {
if (!success) g_appMetadataCount = orig_app_count; if (!success) g_appMetadataCount = orig_app_count;
@ -1500,14 +1578,13 @@ end:
} }
} }
/* Remove gamecard title info entries and close its ncm database and storage handles (if needed). */
if (cleanup) if (cleanup)
{ {
titleRemoveGameCardTitleInfoEntries(); titleRemoveGameCardTitleInfoEntries();
titleCloseNcmDatabaseAndStorageFromGameCard(); titleCloseNcmDatabaseAndStorageFromGameCard();
} }
if (lock) mutexUnlock(&g_titleMutex);
return success; return success;
} }

View file

@ -82,10 +82,6 @@ NcmContentMetaDatabase *titleGetNcmDatabaseByStorageId(u8 storage_id);
/// Returns a pointer to a ncm storage handle using a NcmStorageId value. /// Returns a pointer to a ncm storage handle using a NcmStorageId value.
NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id); NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id);
/// Returns true if gamecard title info has been (un)loaded.
/// Suitable for being called between UI updates.
bool titleRefreshGameCardTitleInfo(void);
/// Returns a pointer to a dynamically allocated buffer of pointers to TitleApplicationMetadata entries, as well as their count. The allocated buffer must be freed by the calling function. /// Returns a pointer to a dynamically allocated buffer of pointers to TitleApplicationMetadata entries, as well as their count. The allocated buffer must be freed by the calling function.
/// If 'is_system' is true, TitleApplicationMetadata entries from available system titles (NcmStorageId_BuiltInSystem) will be returned. /// If 'is_system' is true, TitleApplicationMetadata entries from available system titles (NcmStorageId_BuiltInSystem) will be returned.
/// Otherwise, TitleApplicationMetadata entries from user applications with available content data (NcmStorageId_Any) will be returned. /// Otherwise, TitleApplicationMetadata entries from user applications with available content data (NcmStorageId_Any) will be returned.
@ -100,6 +96,10 @@ TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
/// Populates a TitleUserApplicationData element using an user application ID. /// Populates a TitleUserApplicationData element using an user application ID.
bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out); bool titleGetUserApplicationData(u64 app_id, TitleUserApplicationData *out);
/// Returns true if the gamecard title info entries have been updated (e.g. after a new gamecard has been inserted, of after the current one has been taken out).
/// If titleGetApplicationMetadataEntries() has been previously called, its returned buffer should be freed and a new titleGetApplicationMetadataEntries() call should be issued.
bool titleIsGameCardInfoUpdated(void);
/// Returns a pointer to a string holding the name of the provided ncm content type. /// Returns a pointer to a string holding the name of the provided ncm content type.
const char *titleGetNcmContentTypeName(u8 content_type); const char *titleGetNcmContentTypeName(u8 content_type);

View file

@ -19,7 +19,6 @@ todo:
bktr: filelist generation functions (wrappers for romfs filelist generation functions) bktr: filelist generation functions (wrappers for romfs filelist generation functions)
title: move gamecard stuff to another thread?
title: more functions for title lookup (filters, patches / aoc, etc.) title: more functions for title lookup (filters, patches / aoc, etc.)
title: more functions for content lookup (based on id?) title: more functions for content lookup (based on id?)
title: find a nice way to deal with *true* orphan content (no ns records from parent base game) title: find a nice way to deal with *true* orphan content (no ns records from parent base game)