mirror of
https://github.com/DarkMatterCore/nxdumptool.git
synced 2024-11-22 18:26:39 +00:00
Threaded gamecard title info/metadata retrieval.
This commit is contained in:
parent
cd8afd2cd8
commit
bb8cba1eaa
4 changed files with 161 additions and 53 deletions
|
@ -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 ((selected_idx + 1) < app_count)
|
||||
if ((btn_down & KEY_DDOWN) || (btn_held & (KEY_LSTICK_DOWN | KEY_RSTICK_DOWN)))
|
||||
{
|
||||
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 {
|
||||
selected_idx = 0;
|
||||
}
|
||||
} else
|
||||
if (btn & KEY_UP)
|
||||
if (selected_idx < cur_page)
|
||||
{
|
||||
if (selected_idx == 0)
|
||||
{
|
||||
selected_idx = (app_count - 1);
|
||||
} else {
|
||||
selected_idx--;
|
||||
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:
|
||||
if (exit_prompt)
|
||||
{
|
||||
consolePrint("press any button to exit\n");
|
||||
utilsWaitForButtonPress(KEY_NONE);
|
||||
}
|
||||
|
||||
bktrFreeContext(&bktr_ctx);
|
||||
|
||||
|
|
121
source/title.c
121
source/title.c
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
1
todo.txt
1
todo.txt
|
@ -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)
|
||||
|
|
Loading…
Reference in a new issue