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

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;
}
u32 app_count = 0, selected_idx = 0;
u32 app_count = 0;
TitleApplicationMetadata **app_metadata = NULL;
TitleUserApplicationData user_app_data = {0};
u32 selected_idx = 0, page_size = 30, cur_page = 0;
bool exit_prompt = true;
u8 *buf = NULL;
NcaContext *base_nca_ctx = NULL, *update_nca_ctx = NULL;
@ -268,21 +271,27 @@ int main(int argc, char *argv[])
while(true)
{
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);
u64 btn = 0;
u64 btn_down = 0, btn_held = 0;
while(true)
{
hidScanInput();
btn_down = utilsHidKeysAllDown();
btn_held = utilsHidKeysAllHeld();
if (btn_down || btn_held) break;
btn = utilsHidKeysAllDown();
if (btn) break;
if (titleRefreshGameCardTitleInfo())
if (titleIsGameCardInfoUpdated())
{
free(app_metadata);
@ -293,13 +302,12 @@ int main(int argc, char *argv[])
goto out2;
}
selected_idx = 0;
selected_idx = cur_page = 0;
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)
{
@ -310,29 +318,50 @@ int main(int argc, char *argv[])
break;
} 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++;
if (selected_idx >= app_count)
{
selected_idx++;
} else {
selected_idx = 0;
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 & KEY_UP)
if ((btn_down & KEY_DUP) || (btn_held & (KEY_LSTICK_UP | KEY_RSTICK_UP)))
{
if (selected_idx == 0)
selected_idx--;
if (selected_idx == UINT32_MAX)
{
selected_idx = (app_count - 1);
} else {
selected_idx--;
if (btn_down & KEY_DUP)
{
selected_idx = (app_count - 1);
cur_page = (app_count - (app_count % page_size));
} else {
selected_idx = 0;
}
} else
if (selected_idx < cur_page)
{
cur_page -= page_size;
}
} else
if (btn & KEY_B)
if (btn_down & KEY_B)
{
consolePrint("\nprocess cancelled.\n");
exit_prompt = false;
goto out2;
}
if (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN | KEY_LSTICK_UP | KEY_RSTICK_UP)) svcSleepThread(50000000); // 50 ms
}
consoleClear();
@ -469,8 +498,11 @@ int main(int argc, char *argv[])
consolePrint("process completed in %lu seconds\n", start);
out2:
consolePrint("press any button to exit\n");
utilsWaitForButtonPress(KEY_NONE);
if (exit_prompt)
{
consolePrint("press any button to exit\n");
utilsWaitForButtonPress(KEY_NONE);
}
bktrFreeContext(&bktr_ctx);

View file

@ -34,7 +34,10 @@ typedef struct {
/* Global variables. */
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;
@ -373,11 +376,15 @@ static void titleCloseNcmStorages(void);
static bool titleOpenNcmDatabaseAndStorageFromGameCard(void);
static void titleCloseNcmDatabaseAndStorageFromGameCard(void);
static bool titleLoadTitleInfo(void);
static bool titleLoadPersistentStorageTitleInfo(void);
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 _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 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. */
if (!titleLoadTitleInfo())
if (!titleLoadPersistentStorageTitleInfo())
{
LOGFILE("Failed to load title info!");
LOGFILE("Failed to load persistent storage title info!");
goto end;
}
/* Initial gamecard title info retrieval. */
_titleRefreshGameCardTitleInfo(false);
/* Create usermode exit event. */
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);
/* Destroy gamecard detection thread. */
if (g_titleGameCardInfoThreadCreated)
{
titleDestroyGameCardInfoThread();
g_titleGameCardInfoThreadCreated = false;
}
/* Free title info. */
titleFreeTitleInfo();
/* Close gamecard ncm database and storage. */
/* Close gamecard ncm database and storage (if needed). */
titleCloseNcmDatabaseAndStorageFromGameCard();
/* Close eMMC System, eMMC User and SD card ncm storages. */
@ -627,11 +647,6 @@ NcmContentStorage *titleGetNcmStorageByStorageId(u8 storage_id)
return ncm_storage;
}
bool titleRefreshGameCardTitleInfo(void)
{
return _titleRefreshGameCardTitleInfo(true);
}
TitleApplicationMetadata **titleGetApplicationMetadataEntries(bool is_system, u32 *out_count)
{
mutexLock(&g_titleMutex);
@ -743,6 +758,15 @@ end:
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)
{
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);
}
static bool titleLoadTitleInfo(void)
static bool titleLoadPersistentStorageTitleInfo(void)
{
/* Return right away if title info has already been retrieved. */
if (g_titleInfo || g_titleInfoCount) return true;
@ -1384,10 +1408,64 @@ end:
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;
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;
@ -1487,7 +1565,7 @@ end:
/* Update gamecard 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 = 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)
{
titleRemoveGameCardTitleInfoEntries();
titleCloseNcmDatabaseAndStorageFromGameCard();
}
if (lock) mutexUnlock(&g_titleMutex);
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.
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.
/// 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.
@ -100,6 +96,10 @@ TitleInfo *titleGetInfoFromStorageByTitleId(u8 storage_id, u64 title_id);
/// Populates a TitleUserApplicationData element using an user application ID.
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.
const char *titleGetNcmContentTypeName(u8 content_type);

View file

@ -19,7 +19,6 @@ todo:
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 content lookup (based on id?)
title: find a nice way to deal with *true* orphan content (no ns records from parent base game)