diff --git a/source/main.c b/source/main.c index f0bbe66..5c7ca19 100644 --- a/source/main.c +++ b/source/main.c @@ -7,15 +7,15 @@ #include "util.h" #include "libkhax/khax.h" #include "ui/mainmenu.h" -#include "ui/section/task.h" #include "ui/section/action/clipboard.h" +#include "ui/section/task/task.h" static void* soc_buffer; void cleanup() { clipboard_clear(); - task_exit(); + task_quit_all(); screen_exit(); socExit(); @@ -44,6 +44,15 @@ int main(int argc, const char* argv[]) { } } + aptOpenSession(); + Result setCpuTimeRes = APT_SetAppCpuTimeLimit(30); + aptCloseSession(); + + if(R_FAILED(setCpuTimeRes)) { + util_panic("Failed to set syscore CPU time: %08lX", setCpuTimeRes); + return 1; + } + romfsInit(); cfguInit(); acInit(); @@ -58,7 +67,6 @@ int main(int argc, const char* argv[]) { } screen_init(); - task_init(); mainmenu_open(); diff --git a/source/ui/error.c b/source/ui/error.c index 27a57cc..1702d1d 100644 --- a/source/ui/error.c +++ b/source/ui/error.c @@ -27,7 +27,20 @@ static void error_onresponse(ui_view* view, void* data, bool response) { free(data); } -void error_display_res(void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...) { +void error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...) { + error_data* errorData = (error_data*) calloc(1, sizeof(error_data)); + errorData->data = data; + errorData->drawTop = drawTop; + + va_list list; + va_start(list, text); + vsnprintf(errorData->fullText, 4096, text, list); + va_end(list); + + ui_push(prompt_create("Error", errorData->fullText, 0xFF000000, false, errorData, NULL, error_draw_top, error_onresponse)); +} + +void error_display_res(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...) { error_data* errorData = (error_data*) calloc(1, sizeof(error_data)); errorData->data = data; errorData->drawTop = drawTop; diff --git a/source/ui/error.h b/source/ui/error.h index 3bb3086..a961503 100644 --- a/source/ui/error.h +++ b/source/ui/error.h @@ -2,5 +2,6 @@ #include "ui.h" +void error_display(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...); void error_display_res(void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...); void error_display_errno(void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), int err, const char* text, ...); \ No newline at end of file diff --git a/source/ui/list.c b/source/ui/list.c index 5b3d226..939c52d 100644 --- a/source/ui/list.c +++ b/source/ui/list.c @@ -2,7 +2,6 @@ #include #include "list.h" -#include "section/task.h" #include "../screen.h" typedef struct { @@ -73,8 +72,6 @@ static void list_validate_pos(list_data* listData, float by1, float by2) { } static void list_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - list_data* listData = (list_data*) data; bool selectedTouched = false; @@ -170,25 +167,17 @@ static void list_update(ui_view* view, void* data, float bx1, float by1, float b if(listData->update != NULL) { listData->update(view, listData->data, &listData->items, &listData->itemCount, listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0 ? &listData->items[listData->selectedIndex] : NULL, selectedTouched); } - - svcReleaseMutex(task_get_mutex()); } static void list_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - list_data* listData = (list_data*) data; if(listData->drawTop != NULL) { listData->drawTop(view, listData->data, x1, y1, x2, y2, listData->items != NULL && listData->itemCount != NULL && *listData->itemCount > 0 ? &listData->items[listData->selectedIndex] : NULL); } - - svcReleaseMutex(task_get_mutex()); } static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - list_data* listData = (list_data*) data; list_validate_pos(listData, y1, y2); @@ -218,8 +207,6 @@ static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, floa y += stringHeight; } } - - svcReleaseMutex(task_get_mutex()); } ui_view* list_create(const char* name, const char* info, void* data, void (*update)(ui_view* view, void* data, list_item** contents, u32** itemCount, list_item* selected, bool selectedTouched), diff --git a/source/ui/progressbar.c b/source/ui/progressbar.c index 34aa33f..18523ae 100644 --- a/source/ui/progressbar.c +++ b/source/ui/progressbar.c @@ -1,7 +1,6 @@ #include <3ds.h> #include -#include "section/task.h" #include "progressbar.h" #include "../screen.h" @@ -15,27 +14,19 @@ typedef struct { } progressbar_data; static void progressbar_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - progressbar_data* progressBarData = (progressbar_data*) data; if(progressBarData->update != NULL) { progressBarData->update(view, progressBarData->data, &progressBarData->progress, progressBarData->progressText); } - - svcReleaseMutex(task_get_mutex()); } static void progressbar_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - progressbar_data* progressBarData = (progressbar_data*) data; if(progressBarData->drawTop != NULL) { progressBarData->drawTop(view, progressBarData->data, x1, y1, x2, y2); } - - svcReleaseMutex(task_get_mutex()); } static void progressbar_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) { diff --git a/source/ui/prompt.c b/source/ui/prompt.c index 539eed4..294296e 100644 --- a/source/ui/prompt.c +++ b/source/ui/prompt.c @@ -1,7 +1,6 @@ #include <3ds.h> #include -#include "section/task.h" #include "prompt.h" #include "../screen.h" @@ -24,22 +23,16 @@ static void notify_response(ui_view* view, prompt_data* promptData, bool respons } static void prompt_update(ui_view* view, void* data, float bx1, float by1, float bx2, float by2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - prompt_data* promptData = (prompt_data*) data; if(promptData->onResponse != NULL) { if(!promptData->option && (hidKeysDown() & ~KEY_TOUCH)) { notify_response(view, promptData, false); - - svcReleaseMutex(task_get_mutex()); return; } if(promptData->option && (hidKeysDown() & (KEY_A | KEY_B))) { notify_response(view, promptData, (bool) (hidKeysDown() & KEY_A)); - - svcReleaseMutex(task_get_mutex()); return; } @@ -56,8 +49,6 @@ static void prompt_update(ui_view* view, void* data, float bx1, float by1, float float yesButtonY = by2 - 5 - buttonHeight; if(pos.px >= yesButtonX && pos.py >= yesButtonY && pos.px < yesButtonX + buttonWidth && pos.py < yesButtonY + buttonHeight) { notify_response(view, promptData, true); - - svcReleaseMutex(task_get_mutex()); return; } @@ -65,8 +56,6 @@ static void prompt_update(ui_view* view, void* data, float bx1, float by1, float float noButtonY = by2 - 5 - buttonHeight; if(pos.px >= noButtonX && pos.py >= noButtonY && pos.px < noButtonX + buttonWidth && pos.py < noButtonY + buttonHeight) { notify_response(view, promptData, false); - - svcReleaseMutex(task_get_mutex()); return; } } else { @@ -78,8 +67,6 @@ static void prompt_update(ui_view* view, void* data, float bx1, float by1, float float okayButtonY = by2 - 5 - buttonHeight; if(pos.px >= okayButtonX && pos.py >= okayButtonY && pos.px < okayButtonX + buttonWidth && pos.py < okayButtonY + buttonHeight) { notify_response(view, promptData, false); - - svcReleaseMutex(task_get_mutex()); return; } } @@ -89,20 +76,14 @@ static void prompt_update(ui_view* view, void* data, float bx1, float by1, float if(promptData->update != NULL) { promptData->update(view, promptData->data); } - - svcReleaseMutex(task_get_mutex()); } static void prompt_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - svcWaitSynchronization(task_get_mutex(), U64_MAX); - prompt_data* promptData = (prompt_data*) data; if(promptData->drawTop != NULL) { promptData->drawTop(view, promptData->data, x1, y1, x2, y2); } - - svcReleaseMutex(task_get_mutex()); } static void prompt_draw_bottom(ui_view* view, void* data, float x1, float y1, float x2, float y2) { diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index df229c3..3a156c2 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -1,6 +1,6 @@ #pragma once -#include "../task.h" +#include "../task/task.h" void action_browse_ext_save_data(ext_save_data_info* info); diff --git a/source/ui/section/action/clipboard.c b/source/ui/section/action/clipboard.c index fa8c2f3..9611560 100644 --- a/source/ui/section/action/clipboard.c +++ b/source/ui/section/action/clipboard.c @@ -3,7 +3,6 @@ #include #include <3ds.h> -#include <3ds/services/fs.h> #include "clipboard.h" diff --git a/source/ui/section/action/copyfiles.c b/source/ui/section/action/copyfiles.c index 08a7502..423531c 100644 --- a/source/ui/section/action/copyfiles.c +++ b/source/ui/section/action/copyfiles.c @@ -5,7 +5,6 @@ #include "../../error.h" #include "../../prompt.h" - static void action_copy_files_success_onresponse(ui_view* view, void* data, bool response) { prompt_destroy(view); } diff --git a/source/ui/section/action/deleteallpendingtitles.c b/source/ui/section/action/deleteallpendingtitles.c index d5577f9..fb1209c 100644 --- a/source/ui/section/action/deleteallpendingtitles.c +++ b/source/ui/section/action/deleteallpendingtitles.c @@ -6,8 +6,6 @@ #include "../../prompt.h" static void action_delete_all_pending_titles_success_onresponse(ui_view* view, void* data, bool response) { - task_refresh_pending_titles(); - prompt_destroy(view); } diff --git a/source/ui/section/action/deletecontents.c b/source/ui/section/action/deletecontents.c index 927d907..377cfd1 100644 --- a/source/ui/section/action/deletecontents.c +++ b/source/ui/section/action/deletecontents.c @@ -29,15 +29,6 @@ static void action_delete_dir_contents_free_data(delete_dir_contents_data* data) static void action_delete_dir_contents_done_onresponse(ui_view* view, void* data, bool response) { action_delete_dir_contents_free_data((delete_dir_contents_data*) data); - if(task_get_files_path() != NULL && !util_is_dir(task_get_files_archive(), task_get_files_path())) { - char parentPath[PATH_MAX]; - util_get_parent_path(parentPath, task_get_files_path(), PATH_MAX); - - strncpy(task_get_files_path(), parentPath, PATH_MAX); - } - - task_refresh_files(); - prompt_destroy(view); } diff --git a/source/ui/section/action/deletependingtitle.c b/source/ui/section/action/deletependingtitle.c index c4c5a18..16c38b0 100644 --- a/source/ui/section/action/deletependingtitle.c +++ b/source/ui/section/action/deletependingtitle.c @@ -6,8 +6,6 @@ #include "../../prompt.h" static void action_delete_pending_title_success_onresponse(ui_view* view, void* data, bool response) { - task_refresh_pending_titles(); - prompt_destroy(view); } diff --git a/source/ui/section/action/deleteticket.c b/source/ui/section/action/deleteticket.c index 1bd8018..9cd746b 100644 --- a/source/ui/section/action/deleteticket.c +++ b/source/ui/section/action/deleteticket.c @@ -6,8 +6,6 @@ #include "../../prompt.h" static void action_delete_ticket_success_onresponse(ui_view* view, void* data, bool response) { - task_refresh_tickets(); - prompt_destroy(view); } diff --git a/source/ui/section/action/deletetitle.c b/source/ui/section/action/deletetitle.c index 3533a6c..84f6a7d 100644 --- a/source/ui/section/action/deletetitle.c +++ b/source/ui/section/action/deletetitle.c @@ -6,8 +6,6 @@ #include "../../prompt.h" static void action_delete_title_success_onresponse(ui_view* view, void* data, bool response) { - task_refresh_pending_titles(); - prompt_destroy(view); } diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index 69b5601..548e25e 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -5,6 +5,7 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../progressbar.h" #include "../../prompt.h" @@ -20,6 +21,9 @@ typedef struct { u32 processed; u32 total; char** contents; + + install_cia_result installResult; + Handle installCancelEvent; } install_cias_data; static Result action_install_cias_read(void* data, u32* bytesRead, void* buffer, u32 size) { @@ -50,60 +54,55 @@ static void action_install_cias_free_data(install_cias_data* data) { static void action_install_cias_done_onresponse(ui_view* view, void* data, bool response) { action_install_cias_free_data((install_cias_data*) data); - task_refresh_titles(); - prompt_destroy(view); } static void action_install_cias_update(ui_view* view, void* data, float* progress, char* progressText) { install_cias_data* installData = (install_cias_data*) data; - bool cancelled = false; if(hidKeysDown() & KEY_B) { - task_cancel_cia_install(); - - while(task_is_cia_installing()) { + svcSignalEvent(installData->installCancelEvent); + while(svcWaitSynchronization(installData->installCancelEvent, 0) == 0) { svcSleepThread(1000000); } - cancelled = true; + installData->installCancelEvent = 0; } - if(!task_is_cia_installing()) { + if(!installData->installStarted || installData->installResult.finished) { char* path = installData->contents[installData->processed]; - if(installData->installStarted || cancelled) { - if(installData->currHandle != 0) { - FSFILE_Close(installData->currHandle); - installData->currHandle = 0; - } + if(installData->currHandle != 0) { + FSFILE_Close(installData->currHandle); + installData->currHandle = 0; + } - Result res = task_get_cia_install_result(); - if(R_FAILED(res)) { - if(installData->processed >= installData->total - 1) { + if(installData->installResult.finished) { + if(installData->installResult.failed) { + if(installData->installResult.cancelled || installData->processed >= installData->total - 1) { ui_pop(); } - if(res == CIA_INSTALL_RESULT_CANCELLED) { + if(installData->installResult.cancelled) { ui_push(prompt_create("Failure", "Install cancelled.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); - } else if(res == CIA_INSTALL_RESULT_ERRNO) { + } else if(installData->installResult.ioerr) { if(strlen(path) > 48) { - error_display_errno(installData->base, ui_draw_file_info, task_get_cia_install_errno(), "Failed to install CIA file.\n%.45s...", path); + error_display_errno(installData->base, ui_draw_file_info, installData->installResult.ioerrno, "Failed to install CIA file.\n%.45s...", path); } else { - error_display_errno(installData->base, ui_draw_file_info, task_get_cia_install_errno(), "Failed to install CIA file.\n%.48s", path); + error_display_errno(installData->base, ui_draw_file_info, installData->installResult.ioerrno, "Failed to install CIA file.\n%.48s", path); } - } else if(res == CIA_INSTALL_RESULT_WRONG_SYSTEM) { + } else if(installData->installResult.wrongSystem) { ui_push(prompt_create("Failure", "Attempted to install to wrong system.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); } else { if(strlen(path) > 48) { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.45s...", path); + error_display_res(installData->base, ui_draw_file_info, installData->installResult.result, "Failed to install CIA file.\n%.45s...", path); } else { - error_display_res(installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.48s", path); + error_display_res(installData->base, ui_draw_file_info, installData->installResult.result, "Failed to install CIA file.\n%.48s", path); } } - if(installData->processed >= installData->total - 1) { - if(res != CIA_INSTALL_RESULT_CANCELLED && res != CIA_INSTALL_RESULT_WRONG_SYSTEM) { + if(installData->installResult.cancelled || installData->processed >= installData->total - 1) { + if(!installData->installResult.cancelled && !installData->installResult.wrongSystem) { action_install_cias_free_data(installData); } @@ -118,8 +117,8 @@ static void action_install_cias_update(ui_view* view, void* data, float* progres installData->installStarted = true; if(installData->processed >= installData->total) { - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); ui_push(prompt_create("Success", "Install finished.", 0xFF000000, false, data, NULL, action_install_cias_draw_top, action_install_cias_done_onresponse)); return; @@ -134,7 +133,12 @@ static void action_install_cias_update(ui_view* view, void* data, float* progres if(R_SUCCEEDED(res = FSFILE_GetSize(installData->currHandle, &size))) { installData->currTotal = size; - task_request_cia_install(installData->dest, installData->currTotal, installData, action_install_cias_read); + installData->installCancelEvent = task_install_cia(&installData->installResult, installData->dest, installData->currTotal, installData, action_install_cias_read); + if(installData->installCancelEvent == 0) { + ui_pop(); + progressbar_destroy(view); + return; + } } } diff --git a/source/ui/section/action/pastefiles.c b/source/ui/section/action/pastefiles.c index 95843e7..db3c01f 100644 --- a/source/ui/section/action/pastefiles.c +++ b/source/ui/section/action/pastefiles.c @@ -3,7 +3,6 @@ #include #include <3ds.h> -#include <3ds/services/fs.h> #include "action.h" #include "clipboard.h" @@ -11,7 +10,6 @@ #include "../../progressbar.h" #include "../../prompt.h" #include "../../../util.h" -#include "../task.h" typedef struct { file_info* base; @@ -36,8 +34,6 @@ static void action_paste_files_free_data(paste_files_data* data) { static void action_paste_files_done_onresponse(ui_view* view, void* data, bool response) { action_paste_files_free_data((paste_files_data*) data); - task_refresh_files(); - prompt_destroy(view); } diff --git a/source/ui/section/extsavedata.c b/source/ui/section/extsavedata.c index 73bf75a..cc25ff2 100644 --- a/source/ui/section/extsavedata.c +++ b/source/ui/section/extsavedata.c @@ -4,8 +4,18 @@ #include <3ds.h> #include "action/action.h" +#include "task/task.h" #include "section.h" +#define EXTSAVEDATA_MAX 512 + +typedef struct { + list_item items[EXTSAVEDATA_MAX]; + u32 count; + Handle cancelEvent; + bool populated; +} extsavedata_data; + #define EXTSAVEDATA_ACTION_COUNT 1 static u32 extsavedata_action_count = EXTSAVEDATA_ACTION_COUNT; @@ -51,26 +61,44 @@ static void extsavedata_draw_top(ui_view* view, void* data, float x1, float y1, } static void extsavedata_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { + extsavedata_data* listData = (extsavedata_data*) data; + if(hidKeysDown() & KEY_B) { - list_destroy(view); ui_pop(); + free(listData); + list_destroy(view); return; } - if(hidKeysDown() & KEY_X) { - task_refresh_ext_save_data(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + listData->cancelEvent = task_populate_ext_save_data(listData->items, &listData->count, EXTSAVEDATA_MAX); + listData->populated = true; } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + listData->populated = false; + ui_push(extsavedata_action_create((ext_save_data_info*) selected->data)); + return; } - if(*itemCount != task_get_ext_save_data_count() || *items != task_get_ext_save_data()) { - *itemCount = task_get_ext_save_data_count(); - *items = task_get_ext_save_data(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void extsavedata_open() { - ui_push(list_create("Ext Save Data", "A: Select, B: Return, X: Refresh", NULL, extsavedata_update, extsavedata_draw_top)); + extsavedata_data* data = (extsavedata_data*) calloc(1, sizeof(extsavedata_data)); + + ui_push(list_create("Ext Save Data", "A: Select, B: Return, X: Refresh", data, extsavedata_update, extsavedata_draw_top)); } \ No newline at end of file diff --git a/source/ui/section/files.c b/source/ui/section/files.c index cdd4cea..80c25c4 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -4,16 +4,21 @@ #include #include <3ds.h> -#include <3ds/services/fs.h> #include "action/action.h" -#include "section.h" -#include "../error.h" +#include "task/task.h" #include "../../util.h" -#include "task.h" +#include "../error.h" +#include "section.h" + +#define FILES_MAX 1024 typedef struct { - bool setup; + list_item items[FILES_MAX]; + u32 count; + Handle cancelEvent; + bool populated; + FS_Archive archive; void* archivePath; char path[PATH_MAX]; @@ -122,33 +127,53 @@ static void files_draw_top(ui_view* view, void* data, float x1, float y1, float } } +static void files_repopulate(files_data* listData) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + if(!util_is_dir(&listData->archive, listData->path)) { + char parentPath[PATH_MAX]; + util_get_parent_path(parentPath, listData->path, PATH_MAX); + + strncpy(listData->path, parentPath, PATH_MAX); + } + + listData->cancelEvent = task_populate_files(listData->items, &listData->count, FILES_MAX, &listData->archive, listData->path); + listData->populated = true; +} + static void files_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { - files_data* filesData = (files_data*) data; + files_data* listData = (files_data*) data; if(hidKeysDown() & KEY_B) { - if(strcmp(filesData->path, "/") == 0) { - if(filesData->archive.handle != 0) { - FSUSER_CloseArchive(&filesData->archive); - filesData->archive.handle = 0; + if(strcmp(listData->path, "/") == 0) { + if(listData->archive.handle != 0) { + FSUSER_CloseArchive(&listData->archive); + listData->archive.handle = 0; } - if(filesData->archivePath != NULL) { - free(filesData->archivePath); - filesData->archivePath = NULL; + if(listData->archivePath != NULL) { + free(listData->archivePath); + listData->archivePath = NULL; } - free(data); - list_destroy(view); - ui_pop(); + free(listData); + list_destroy(view); return; } else if(*items != NULL && *itemCount != NULL) { for(u32 i = 0; i < **itemCount; i++) { char* name = (*items)[i].name; file_info* fileInfo = (*items)[i].data; if(fileInfo != NULL && strcmp(name, "..") == 0) { - strncpy(filesData->path, fileInfo->path, PATH_MAX); - task_refresh_files(); + strncpy(listData->path, fileInfo->path, PATH_MAX); + files_repopulate(listData); break; } } @@ -159,42 +184,38 @@ static void files_update(ui_view* view, void* data, list_item** items, u32** ite file_info* fileInfo = (file_info*) selected->data; if(strcmp(selected->name, ".") == 0) { + listData->populated = false; + ui_push(files_action_create(fileInfo)); + return; } else if(strcmp(selected->name, "..") == 0) { - strncpy(filesData->path, fileInfo->path, PATH_MAX); - task_refresh_files(); + strncpy(listData->path, fileInfo->path, PATH_MAX); + files_repopulate(listData); } else { - if(util_is_dir(&filesData->archive, fileInfo->path)) { - strncpy(filesData->path, fileInfo->path, PATH_MAX); - task_refresh_files(); + if(util_is_dir(&listData->archive, fileInfo->path)) { + strncpy(listData->path, fileInfo->path, PATH_MAX); + files_repopulate(listData); } else { + listData->populated = false; + ui_push(files_action_create(fileInfo)); + return; } } } - if(hidKeysDown() & KEY_X) { - task_refresh_files(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + files_repopulate(listData); } - if(!filesData->setup || task_get_files_archive() != &filesData->archive || task_get_files_path() != filesData->path) { - filesData->setup = true; - - task_set_files_archive(&filesData->archive); - task_set_files_path(filesData->path); - - task_refresh_files(); - } - - if(*itemCount != task_get_files_count() || *items != task_get_files()) { - *itemCount = task_get_files_count(); - *items = task_get_files(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void files_open(FS_Archive archive) { files_data* data = (files_data*) calloc(1, sizeof(files_data)); - data->setup = false; data->archive = archive; snprintf(data->path, PATH_MAX, "/"); diff --git a/source/ui/section/networkinstall.c b/source/ui/section/networkinstall.c index df21002..7051b83 100644 --- a/source/ui/section/networkinstall.c +++ b/source/ui/section/networkinstall.c @@ -8,12 +8,13 @@ #include <3ds.h> #include "action/action.h" -#include "section.h" +#include "task/task.h" #include "../error.h" #include "../progressbar.h" #include "../prompt.h" #include "../../screen.h" #include "../../util.h" +#include "section.h" typedef struct { FS_MediaType dest; @@ -24,6 +25,9 @@ typedef struct { u64 currTotal; u32 processed; u32 total; + + install_cia_result installResult; + Handle installCancelEvent; } network_install_data; static int recvwait(int sockfd, void* buf, size_t len, int flags) { @@ -69,8 +73,6 @@ static Result networkinstall_read(void* data, u32* bytesRead, void* buffer, u32 } static void networkinstall_done_onresponse(ui_view* view, void* data, bool response) { - task_refresh_titles(); - prompt_destroy(view); } @@ -89,34 +91,31 @@ static void networkinstall_close_client(network_install_data* data) { static void networkinstall_install_update(ui_view* view, void* data, float* progress, char* progressText) { network_install_data* networkInstallData = (network_install_data*) data; - bool cancelled = false; if(hidKeysDown() & KEY_B) { - task_cancel_cia_install(); - - while(task_is_cia_installing()) { + svcSignalEvent(networkInstallData->installCancelEvent); + while(svcWaitSynchronization(networkInstallData->installCancelEvent, 0) == 0) { svcSleepThread(1000000); } - cancelled = true; + networkInstallData->installCancelEvent = 0; } - if(!task_is_cia_installing()) { - if(networkInstallData->installStarted || cancelled) { - Result res = task_get_cia_install_result(); - if(R_FAILED(res)) { + if(!networkInstallData->installStarted || networkInstallData->installResult.finished) { + if(networkInstallData->installResult.finished) { + if(networkInstallData->installResult.failed) { networkinstall_close_client(networkInstallData); - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); - if(res == CIA_INSTALL_RESULT_CANCELLED) { + if(networkInstallData->installResult.cancelled) { ui_push(prompt_create("Failure", "Install cancelled.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse)); - } else if(res == CIA_INSTALL_RESULT_ERRNO) { - error_display_errno(NULL, NULL, task_get_cia_install_errno(), "Failed to install CIA file."); - } else if(res == CIA_INSTALL_RESULT_WRONG_SYSTEM) { + } else if(networkInstallData->installResult.ioerr) { + error_display_errno(NULL, NULL, networkInstallData->installResult.ioerrno, "Failed to install CIA file."); + } else if(networkInstallData->installResult.wrongSystem) { ui_push(prompt_create("Failure", "Attempted to install to wrong system.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse)); } else { - error_display_res(NULL, NULL, res, "Failed to install CIA file."); + error_display_res(NULL, NULL, networkInstallData->installResult.result, "Failed to install CIA file."); } return; @@ -130,8 +129,8 @@ static void networkinstall_install_update(ui_view* view, void* data, float* prog if(networkInstallData->processed >= networkInstallData->total) { networkinstall_close_client(networkInstallData); - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); ui_push(prompt_create("Success", "Install finished.", 0xFF000000, false, data, NULL, NULL, networkinstall_done_onresponse)); return; @@ -143,8 +142,8 @@ static void networkinstall_install_update(ui_view* view, void* data, float* prog if(sendwait(networkInstallData->clientSocket, &ack, sizeof(ack), 0) < 0) { networkinstall_close_client(networkInstallData); - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); error_display_errno(NULL, NULL, errno, "Failed to write CIA accept notification."); return; @@ -153,15 +152,20 @@ static void networkinstall_install_update(ui_view* view, void* data, float* prog if(recvwait(networkInstallData->clientSocket, &networkInstallData->currTotal, sizeof(networkInstallData->currTotal), 0) < 0) { networkinstall_close_client(networkInstallData); - progressbar_destroy(view); ui_pop(); + progressbar_destroy(view); error_display_errno(NULL, NULL, errno, "Failed to read file size."); return; } networkInstallData->currTotal = __builtin_bswap64(networkInstallData->currTotal); - task_request_cia_install(networkInstallData->dest, networkInstallData->currTotal, networkInstallData, networkinstall_read); + networkInstallData->installCancelEvent = task_install_cia(&networkInstallData->installResult, networkInstallData->dest, networkInstallData->currTotal, networkInstallData, networkinstall_read); + if(networkInstallData->installCancelEvent == 0) { + ui_pop(); + progressbar_destroy(view); + return; + } } } diff --git a/source/ui/section/pendingtitles.c b/source/ui/section/pendingtitles.c index 4bea799..1004b09 100644 --- a/source/ui/section/pendingtitles.c +++ b/source/ui/section/pendingtitles.c @@ -4,8 +4,18 @@ #include <3ds.h> #include "action/action.h" +#include "task/task.h" #include "section.h" +#define PENDINGTITLES_MAX 1024 + +typedef struct { + list_item items[PENDINGTITLES_MAX]; + u32 count; + Handle cancelEvent; + bool populated; +} pendingtitles_data; + #define PENDINGTITLES_ACTION_COUNT 2 static u32 pending_titles_action_count = PENDINGTITLES_ACTION_COUNT; @@ -52,26 +62,44 @@ static void pendingtitles_draw_top(ui_view* view, void* data, float x1, float y1 } static void pendingtitles_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { + pendingtitles_data* listData = (pendingtitles_data*) data; + if(hidKeysDown() & KEY_B) { - list_destroy(view); ui_pop(); + free(listData); + list_destroy(view); return; } - if(hidKeysDown() & KEY_X) { - task_refresh_pending_titles(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + listData->cancelEvent = task_populate_pending_titles(listData->items, &listData->count, PENDINGTITLES_MAX); + listData->populated = true; } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + listData->populated = false; + ui_push(pendingtitles_action_create((pending_title_info*) selected->data)); + return; } - if(*itemCount != task_get_pending_title_count() || *items != task_get_pending_titles()) { - *itemCount = task_get_pending_title_count(); - *items = task_get_pending_titles(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void pendingtitles_open() { - ui_push(list_create("Pending Titles", "A: Select, B: Return, X: Refresh", NULL, pendingtitles_update, pendingtitles_draw_top)); + pendingtitles_data* data = (pendingtitles_data*) calloc(1, sizeof(pendingtitles_data)); + + ui_push(list_create("Pending Titles", "A: Select, B: Return, X: Refresh", data, pendingtitles_update, pendingtitles_draw_top)); } \ No newline at end of file diff --git a/source/ui/section/systemsavedata.c b/source/ui/section/systemsavedata.c index 6d20af9..765758e 100644 --- a/source/ui/section/systemsavedata.c +++ b/source/ui/section/systemsavedata.c @@ -4,8 +4,18 @@ #include <3ds.h> #include "action/action.h" +#include "task/task.h" #include "section.h" +#define SYSTEMSAVEDATA_MAX 512 + +typedef struct { + list_item items[SYSTEMSAVEDATA_MAX]; + u32 count; + Handle cancelEvent; + bool populated; +} systemsavedata_data; + #define SYSTEMSAVEDATA_ACTION_COUNT 1 static u32 systemsavedata_action_count = SYSTEMSAVEDATA_ACTION_COUNT; @@ -51,26 +61,44 @@ static void systemsavedata_draw_top(ui_view* view, void* data, float x1, float y } static void systemsavedata_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { + systemsavedata_data* listData = (systemsavedata_data*) data; + if(hidKeysDown() & KEY_B) { - list_destroy(view); ui_pop(); + free(listData); + list_destroy(view); return; } - if(hidKeysDown() & KEY_X) { - task_refresh_system_save_data(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + listData->cancelEvent = task_populate_system_save_data(listData->items, &listData->count, SYSTEMSAVEDATA_MAX); + listData->populated = true; } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + listData->populated = false; + ui_push(systemsavedata_action_create((system_save_data_info*) selected->data)); + return; } - if(*itemCount != task_get_system_save_data_count() || *items != task_get_system_save_data()) { - *itemCount = task_get_system_save_data_count(); - *items = task_get_system_save_data(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void systemsavedata_open() { - ui_push(list_create("System Save Data", "A: Select, B: Return, X: Refresh", NULL, systemsavedata_update, systemsavedata_draw_top)); + systemsavedata_data* data = (systemsavedata_data*) calloc(1, sizeof(systemsavedata_data)); + + ui_push(list_create("System Save Data", "A: Select, B: Return, X: Refresh", data, systemsavedata_update, systemsavedata_draw_top)); } \ No newline at end of file diff --git a/source/ui/section/task.c b/source/ui/section/task.c deleted file mode 100644 index 61d590f..0000000 --- a/source/ui/section/task.c +++ /dev/null @@ -1,1536 +0,0 @@ -#include -#include -#include -#include -#include -#include - -#include <3ds.h> - -#include "task.h" -#include "../error.h" -#include "../../screen.h" -#include "../../util.h" -#include "../list.h" - -#define EVENT_QUIT 0 -#define EVENT_REFRESH_FILES 1 -#define EVENT_REFRESH_TITLES 2 -#define EVENT_REFRESH_PENDING_TITLES 3 -#define EVENT_REFRESH_TICKETS 4 -#define EVENT_REFRESH_EXT_SAVE_DATA 5 -#define EVENT_REFRESH_SYSTEM_SAVE_DATA 6 -#define EVENT_INSTALL_CIA 7 -#define EVENT_COUNT 8 - -#define MAX_ENTRIES 1024 - -typedef struct { - u16 shortDescription[0x40]; - u16 longDescription[0x80]; - u16 publisher[0x40]; -} SMDH_title; - -typedef struct { - char magic[0x04]; - u16 version; - u16 reserved1; - SMDH_title titles[0x10]; - u8 ratings[0x10]; - u32 region; - u32 matchMakerId; - u64 matchMakerBitId; - u32 flags; - u16 eulaVersion; - u16 reserved; - u32 optimalBannerFrame; - u32 streetpassId; - u64 reserved2; - u8 smallIcon[0x480]; - u8 largeIcon[0x1200]; -} SMDH; - -static char* files_path; -static FS_Archive* files_archive; - -static bool files_init = false; -static list_item files_list[MAX_ENTRIES]; -static u32 files_count; - -static bool titles_init = false; -static list_item title_list[MAX_ENTRIES]; -static u32 title_count = 0; - -static bool pending_titles_init = false; -static list_item pending_title_list[MAX_ENTRIES]; -static u32 pending_title_count = 0; - -static bool tickets_init = false; -static list_item ticket_list[MAX_ENTRIES]; -static u32 ticket_count = 0; - -static bool ext_save_data_init = false; -static list_item ext_save_data_list[MAX_ENTRIES]; -static u32 ext_save_data_count = 0; - -static bool system_save_data_init = false; -static list_item system_save_data_list[MAX_ENTRIES]; -static u32 system_save_data_count = 0; - -static bool cia_installing; -static Result cia_result; -static int cia_errno; -static bool cia_cancelled; -static FS_MediaType cia_dest; -static u64 cia_size; -static void* cia_data; -static Result (*cia_read)(void* data, u32* bytesRead, void* buffer, u32 size); - -static Handle events[EVENT_COUNT]; -static Handle mutex; -static Thread task_thread_ptr; - -static int sort_ids(const void* e1, const void* e2) { - u64 id1 = *(u64*) e1; - u64 id2 = *(u64*) e2; - - return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; -} - -static int sort_directory_entries(const void* e1, const void* e2) { - FS_DirectoryEntry* ent1 = (FS_DirectoryEntry*) e1; - FS_DirectoryEntry* ent2 = (FS_DirectoryEntry*) e2; - - if((ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return -1; - } else if(!(ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && (ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { - return 1; - } else { - char entryName1[0x213] = {'\0'}; - utf16_to_utf8((uint8_t*) entryName1, ent1->name, sizeof(entryName1) - 1); - - char entryName2[0x213] = {'\0'}; - utf16_to_utf8((uint8_t*) entryName2, ent2->name, sizeof(entryName2) - 1); - - return strcasecmp(entryName1, entryName2); - } -} - -static void task_clear_files() { - svcWaitSynchronization(mutex, U64_MAX); - files_count = 0; - svcReleaseMutex(mutex); - - for(int i = 0; i < MAX_ENTRIES; i++) { - if(files_list[i].data != NULL) { - file_info* fileInfo = (file_info*) files_list[i].data; - if(fileInfo->isCia && fileInfo->ciaInfo.hasSmdh) { - screen_unload_texture(fileInfo->ciaInfo.smdhInfo.texture); - } - - free(files_list[i].data); - files_list[i].data = NULL; - } - - memset(files_list[i].name, '\0', NAME_MAX); - files_list[i].rgba = 0; - } -} - -static Result task_load_files() { - if(files_archive == NULL || files_path == NULL) { - return 0; - } - - files_init = true; - - file_info* dotFileInfo = (file_info*) calloc(1, sizeof(file_info)); - if(dotFileInfo != NULL) { - dotFileInfo->archive = files_archive; - strncpy(dotFileInfo->path, files_path, PATH_MAX); - util_get_path_file(dotFileInfo->name, dotFileInfo->path, NAME_MAX); - dotFileInfo->isDirectory = true; - dotFileInfo->containsCias = false; - dotFileInfo->size = 0; - dotFileInfo->isCia = false; - - list_item* dotItem = &files_list[files_count]; - strncpy(dotItem->name, ".", NAME_MAX); - dotItem->rgba = 0xFF0000FF; - dotItem->data = dotFileInfo; - - files_count++; - } - - file_info* dotDotFileInfo = (file_info*) calloc(1, sizeof(file_info)); - if(dotDotFileInfo != NULL) { - dotDotFileInfo->archive = files_archive; - util_get_parent_path(dotDotFileInfo->path, files_path, PATH_MAX); - util_get_path_file(dotDotFileInfo->name, dotDotFileInfo->path, NAME_MAX); - dotDotFileInfo->isDirectory = true; - dotDotFileInfo->containsCias = false; - dotDotFileInfo->size = 0; - dotDotFileInfo->isCia = false; - - list_item* dotDotItem = &files_list[files_count]; - strncpy(dotDotItem->name, "..", NAME_MAX); - dotDotItem->rgba = 0xFF0000FF; - dotDotItem->data = dotDotFileInfo; - - files_count++; - } - - Result res = 0; - - Handle dirHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, *files_archive, fsMakePath(PATH_ASCII, files_path)))) { - u32 entryCount = 0; - FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_ENTRIES, sizeof(FS_DirectoryEntry)); - if(entries != NULL) { - if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, MAX_ENTRIES, entries)) && entryCount > 0) { - qsort(entries, entryCount, sizeof(FS_DirectoryEntry), sort_directory_entries); - - SMDH smdh; - for(u32 i = 0; i < entryCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - files_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - if(entries[i].attributes & FS_ATTRIBUTE_HIDDEN) { - continue; - } - - file_info* fileInfo = (file_info*) calloc(1, sizeof(file_info)); - if(fileInfo != NULL) { - char entryName[0x213] = {'\0'}; - utf16_to_utf8((uint8_t*) entryName, entries[i].name, sizeof(entryName) - 1); - - fileInfo->archive = files_archive; - strncpy(fileInfo->name, entryName, NAME_MAX); - - list_item* item = &files_list[files_count]; - - if(entries[i].attributes & FS_ATTRIBUTE_DIRECTORY) { - item->rgba = 0xFF0000FF; - - snprintf(fileInfo->path, PATH_MAX, "%s%s/", files_path, entryName); - fileInfo->isDirectory = true; - fileInfo->containsCias = false; - fileInfo->size = 0; - fileInfo->isCia = false; - } else { - item->rgba = 0xFF000000; - - snprintf(fileInfo->path, PATH_MAX, "%s%s", files_path, entryName); - fileInfo->isDirectory = false; - fileInfo->containsCias = false; - fileInfo->size = 0; - fileInfo->isCia = false; - - Handle fileHandle; - if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *files_archive, fsMakePath(PATH_ASCII, fileInfo->path), FS_OPEN_READ, 0))) { - FSFILE_GetSize(fileHandle, &fileInfo->size); - - AM_TitleEntry titleEntry; - if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) { - dotFileInfo->containsCias = true; - - fileInfo->isCia = true; - fileInfo->ciaInfo.titleId = titleEntry.titleID; - fileInfo->ciaInfo.version = titleEntry.version; - fileInfo->ciaInfo.installedSizeSD = titleEntry.size; - fileInfo->ciaInfo.hasSmdh = false; - - if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) { - fileInfo->ciaInfo.installedSizeNAND = titleEntry.size; - } else { - fileInfo->ciaInfo.installedSizeNAND = 0; - } - - u32 bytesRead; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, fileInfo->size - sizeof(SMDH), &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { - if(smdh.magic[0] == 'S' && smdh.magic[1] == 'M' && smdh.magic[2] == 'D' && smdh.magic[3] == 'H') { - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); - - fileInfo->ciaInfo.hasSmdh = true; - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.smdhInfo.shortDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.smdhInfo.longDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.smdhInfo.publisher) - 1); - fileInfo->ciaInfo.smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); - } - } - } - - FSFILE_Close(fileHandle); - } - } - - strncpy(item->name, entryName, NAME_MAX); - item->data = fileInfo; - - svcWaitSynchronization(mutex, U64_MAX); - files_count++; - svcReleaseMutex(mutex); - } - } - } - - free(entries); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - - FSDIR_Close(dirHandle); - } - - return res; -} - -static void task_clear_titles() { - svcWaitSynchronization(mutex, U64_MAX); - title_count = 0; - svcReleaseMutex(mutex); - - for(u32 i = 0; i < MAX_ENTRIES; i++) { - if(title_list[i].data != NULL) { - title_info* titleInfo = (title_info*) title_list[i].data; - if(titleInfo->hasSmdh) { - screen_unload_texture(titleInfo->smdhInfo.texture); - } - - free(title_list[i].data); - title_list[i].data = NULL; - } - - memset(title_list[i].name, '\0', NAME_MAX); - title_list[i].rgba = 0; - } -} - -static Result task_load_titles(FS_MediaType mediaType) { - titles_init = true; - - if(mediaType == MEDIATYPE_GAME_CARD && R_FAILED(FSUSER_GetCardType(NULL))) { - return 0; - } - - Result res = 0; - - u32 titleCount = 0; - if(R_SUCCEEDED(res = AM_GetTitleCount(mediaType, &titleCount))) { - u64* titleIds = (u64*) calloc(titleCount, sizeof(u64)); - if(titleIds != NULL) { - if(R_SUCCEEDED(res = AM_GetTitleList(&titleCount, mediaType, titleCount, titleIds))) { - qsort(titleIds, titleCount, sizeof(u64), sort_ids); - - AM_TitleEntry* titleInfos = (AM_TitleEntry*) calloc(titleCount, sizeof(AM_TitleEntry)); - if(titleInfos != NULL) { - if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, titleCount, titleIds, titleInfos))) { - SMDH smdh; - for(u32 i = 0; i < titleCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - titles_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info)); - if(titleInfo != NULL) { - titleInfo->mediaType = mediaType; - titleInfo->titleId = titleIds[i]; - AM_GetTitleProductCode(mediaType, titleIds[i], titleInfo->productCode); - titleInfo->version = titleInfos[i].version; - titleInfo->installedSize = titleInfos[i].size; - titleInfo->hasSmdh = false; - - list_item* item = &title_list[title_count]; - - static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; - static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData}; - u32 archivePath[] = {(u32) (titleIds[i] & 0xFFFFFFFF), (u32) ((titleIds[i] >> 32) & 0xFFFFFFFF), mediaType, 0x00000000}; - FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}}; - Handle fileHandle; - if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) { - u32 bytesRead; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { - if(smdh.magic[0] == 'S' && smdh.magic[1] == 'M' && smdh.magic[2] == 'D' && smdh.magic[3] == 'H') { - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); - - utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1); - - titleInfo->hasSmdh = true; - utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(titleInfo->smdhInfo.shortDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(titleInfo->smdhInfo.longDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(titleInfo->smdhInfo.publisher) - 1); - titleInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); - } - } - - FSFILE_Close(fileHandle); - } - - bool empty = strlen(item->name) == 0; - if(!empty) { - empty = true; - - char* curr = item->name; - while(*curr) { - if(*curr != ' ') { - empty = false; - break; - } - - curr++; - } - } - - if(empty) { - snprintf(item->name, NAME_MAX, "%016llX", titleIds[i]); - } - - if(mediaType == MEDIATYPE_NAND) { - item->rgba = 0xFF0000FF; - } else if(mediaType == MEDIATYPE_SD) { - item->rgba = 0xFF00FF00; - } else if(mediaType == MEDIATYPE_GAME_CARD) { - item->rgba = 0xFFFF0000; - } - - item->data = titleInfo; - - svcWaitSynchronization(mutex, U64_MAX); - title_count++; - svcReleaseMutex(mutex); - } - } - } - - free(titleInfos); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - } - - free(titleIds); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - } - - return res; -} - -static void task_clear_pending_titles() { - svcWaitSynchronization(mutex, U64_MAX); - pending_title_count = 0; - svcReleaseMutex(mutex); - - for(u32 i = 0; i < MAX_ENTRIES; i++) { - if(pending_title_list[i].data != NULL) { - free(pending_title_list[i].data); - pending_title_list[i].data = NULL; - } - - memset(pending_title_list[i].name, '\0', NAME_MAX); - pending_title_list[i].rgba = 0; - } -} - -static Result task_load_pending_titles(FS_MediaType mediaType) { - pending_titles_init = true; - - Result res = 0; - - u32 pendingTitleCount = 0; - if(R_SUCCEEDED(res = AM_GetPendingTitleCount(&pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION))) { - u64* pendingTitleIds = (u64*) calloc(pendingTitleCount, sizeof(u64)); - if(pendingTitleIds != NULL) { - if(R_SUCCEEDED(res = AM_GetPendingTitleList(&pendingTitleCount, pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, pendingTitleIds))) { - qsort(pendingTitleIds, pendingTitleCount, sizeof(u64), sort_ids); - - AM_PendingTitleEntry* pendingTitleInfos = (AM_PendingTitleEntry*) calloc(pendingTitleCount, sizeof(AM_PendingTitleEntry)); - if(pendingTitleInfos != NULL) { - if(R_SUCCEEDED(res = AM_GetPendingTitleInfo(pendingTitleCount, mediaType, pendingTitleIds, pendingTitleInfos))) { - for(u32 i = 0; i < pendingTitleCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - pending_titles_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - pending_title_info* pendingTitleInfo = (pending_title_info*) calloc(1, sizeof(pending_title_info)); - if(pendingTitleInfo != NULL) { - pendingTitleInfo->mediaType = mediaType; - pendingTitleInfo->titleId = pendingTitleIds[i]; - pendingTitleInfo->version = pendingTitleInfos[i].version; - - list_item* item = &pending_title_list[pending_title_count]; - snprintf(item->name, NAME_MAX, "%016llX", pendingTitleIds[i]); - if(mediaType == MEDIATYPE_NAND) { - item->rgba = 0xFF0000FF; - } else if(mediaType == MEDIATYPE_SD) { - item->rgba = 0xFF00FF00; - } else if(mediaType == MEDIATYPE_GAME_CARD) { - item->rgba = 0xFFFF0000; - } - - item->data = pendingTitleInfo; - - svcWaitSynchronization(mutex, U64_MAX); - pending_title_count++; - svcReleaseMutex(mutex); - } - } - } - - free(pendingTitleInfos); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - } - - free(pendingTitleIds); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - } - - return res; -} - -static void task_clear_tickets() { - svcWaitSynchronization(mutex, U64_MAX); - ticket_count = 0; - svcReleaseMutex(mutex); - - for(u32 i = 0; i < MAX_ENTRIES; i++) { - if(ticket_list[i].data != NULL) { - free(ticket_list[i].data); - ticket_list[i].data = NULL; - } - - memset(ticket_list[i].name, '\0', NAME_MAX); - ticket_list[i].rgba = 0; - } -} - -static Result task_load_tickets() { - tickets_init = true; - - Result res = 0; - - u32 ticketCount = 0; - if(R_SUCCEEDED(res = AM_GetTicketCount(&ticketCount))) { - u64* ticketIds = (u64*) calloc(ticketCount, sizeof(u64)); - if(ticketIds != NULL) { - if(R_SUCCEEDED(res = AM_GetTicketList(&ticketCount, ticketCount, 0, ticketIds))) { - qsort(ticketIds, ticketCount, sizeof(u64), sort_ids); - - for(u32 i = 0; i < ticketCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - tickets_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - ticket_info* ticketInfo = (ticket_info*) calloc(1, sizeof(ticket_info)); - if(ticketInfo != NULL) { - ticketInfo->ticketId = ticketIds[i]; - - list_item* item = &ticket_list[ticket_count]; - snprintf(item->name, NAME_MAX, "%016llX", ticketIds[i]); - item->rgba = 0xFF000000; - item->data = ticketInfo; - - svcWaitSynchronization(mutex, U64_MAX); - ticket_count++; - svcReleaseMutex(mutex); - } - } - } - - free(ticketIds); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - } - - return res; -} - -static void task_clear_ext_save_data() { - svcWaitSynchronization(mutex, U64_MAX); - ext_save_data_count = 0; - svcReleaseMutex(mutex); - - for(u32 i = 0; i < MAX_ENTRIES; i++) { - if(ext_save_data_list[i].data != NULL) { - ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) ext_save_data_list[i].data; - if(extSaveDataInfo->hasSmdh) { - screen_unload_texture(extSaveDataInfo->smdhInfo.texture); - } - - free(ext_save_data_list[i].data); - ext_save_data_list[i].data = NULL; - } - - memset(ext_save_data_list[i].name, '\0', NAME_MAX); - ext_save_data_list[i].rgba = 0; - } -} - -static Result task_load_ext_save_data(FS_MediaType mediaType) { - ext_save_data_init = true; - - Result res = 0; - - u32 extSaveDataCount = 0; - u64* extSaveDataIds = (u64*) calloc(MAX_ENTRIES, sizeof(u64)); - if(extSaveDataIds != NULL) { - if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, MAX_ENTRIES, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) { - qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), sort_ids); - - SMDH smdh; - for(u32 i = 0; i < extSaveDataCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - ext_save_data_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) calloc(1, sizeof(ext_save_data_info)); - if(extSaveDataInfo != NULL) { - extSaveDataInfo->mediaType = mediaType; - extSaveDataInfo->extSaveDataId = extSaveDataIds[i]; - extSaveDataInfo->shared = mediaType == MEDIATYPE_NAND; - - list_item* item = &ext_save_data_list[ext_save_data_count]; - - FS_ExtSaveDataInfo info = {.mediaType = mediaType, .saveId = extSaveDataIds[i]}; - u32 smdhBytesRead = 0; - if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) &smdh)) && smdhBytesRead == sizeof(SMDH)) { - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); - - utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1); - - extSaveDataInfo->hasSmdh = true; - utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->smdhInfo.shortDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->smdhInfo.longDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->smdhInfo.publisher) - 1); - extSaveDataInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); - } else { - extSaveDataInfo->hasSmdh = false; - } - - bool empty = strlen(item->name) == 0; - if(!empty) { - empty = true; - - char* curr = item->name; - while(*curr) { - if(*curr != ' ') { - empty = false; - break; - } - - curr++; - } - } - - if(empty) { - snprintf(item->name, NAME_MAX, "%016llX", extSaveDataIds[i]); - } - - if(mediaType == MEDIATYPE_NAND) { - item->rgba = 0xFF0000FF; - } else if(mediaType == MEDIATYPE_SD) { - item->rgba = 0xFF00FF00; - } else if(mediaType == MEDIATYPE_GAME_CARD) { - item->rgba = 0xFFFF0000; - } - - item->data = extSaveDataInfo; - - svcWaitSynchronization(mutex, U64_MAX); - ext_save_data_count++; - svcReleaseMutex(mutex); - } - } - } - - free(extSaveDataIds); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - - return res; -} - -static void task_clear_system_save_data() { - svcWaitSynchronization(mutex, U64_MAX); - system_save_data_count = 0; - svcReleaseMutex(mutex); - - for(u32 i = 0; i < MAX_ENTRIES; i++) { - if(system_save_data_list[i].data != NULL) { - free(system_save_data_list[i].data); - system_save_data_list[i].data = NULL; - } - - memset(system_save_data_list[i].name, '\0', NAME_MAX); - system_save_data_list[i].rgba = 0; - } -} - -static Result task_load_system_save_data() { - system_save_data_init = true; - - Result res = 0; - - u32 systemSaveDataCount = 0; - u64* systemSaveDataIds = (u64*) calloc(MAX_ENTRIES, sizeof(u64)); - if(systemSaveDataIds != NULL) { - if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, MAX_ENTRIES * sizeof(u64), systemSaveDataIds))) { - qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u64), sort_ids); - - for(u32 i = 0; i < systemSaveDataCount && i < MAX_ENTRIES; i++) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - system_save_data_init = false; - - svcSignalEvent(events[index]); // Pass on event signal. - break; - } - - system_save_data_info* systemSaveDataInfo = (system_save_data_info*) calloc(1, sizeof(system_save_data_info)); - if(systemSaveDataInfo != NULL) { - systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i]; - - list_item* item = &system_save_data_list[system_save_data_count]; - snprintf(item->name, NAME_MAX, "%016llX", systemSaveDataIds[i]); - item->rgba = 0xFF000000; - item->data = systemSaveDataInfo; - - svcWaitSynchronization(mutex, U64_MAX); - system_save_data_count++; - svcReleaseMutex(mutex); - } - } - } - - free(systemSaveDataIds); - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - - return res; -} - -#define bswap_64(x) \ -({ \ - uint64_t __x = (x); \ - ((uint64_t)( \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ - (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ -}) - -static u32 align(u32 offset, u32 alignment) { - return (offset + (alignment - 1)) & ~(alignment - 1); -} - -static void task_install_cia() { - if(cia_read == NULL) { - return; - } - - Result res = 0; - - cia_result = 0; - cia_errno = 0; - cia_cancelled = false; - cia_installing = true; - - u32 bufferSize = 1024 * 256; - u8* buffer = (u8*) calloc(1, bufferSize); - if(buffer != NULL) { - bool firstBlock = true; - u64 titleId = 0; - - Handle ciaHandle = 0; - - u32 bytesRead = 0; - u32 bytesWritten = 0; - u64 offset = 0; - while(offset < cia_size) { - s32 index = 0; - if(svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, 0) == 0) { - cia_cancelled = true; - svcSignalEvent(events[index]); // Pass on event signal. - } - - if(cia_cancelled) { - cia_cancelled = false; - res = CIA_INSTALL_RESULT_CANCELLED; - break; - } - - u32 readSize = bufferSize; - if(cia_size - offset < readSize) { - readSize = (u32) (cia_size - offset); - } - - if(R_FAILED(res = cia_read(cia_data, &bytesRead, buffer, readSize))) { - if(res == CIA_INSTALL_RESULT_ERRNO) { - cia_errno = errno; - } - - break; - } - - if(firstBlock) { - firstBlock = false; - - u32 headerSize = *(u32*) &buffer[0x00]; - u32 certSize = *(u32*) &buffer[0x08]; - titleId = bswap_64(*(u64*) &buffer[align(headerSize, 64) + align(certSize, 64) + 0x1DC]); - - if((titleId >> 32) & 0x8000) { - cia_dest = MEDIATYPE_NAND; - } - - u8 n3ds = false; - if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { - res = CIA_INSTALL_RESULT_WRONG_SYSTEM; - break; - } - - if(R_FAILED(res = AM_StartCiaInstall(cia_dest, &ciaHandle))) { - break; - } - } - - if(R_FAILED(res = FSFILE_Write(ciaHandle, &bytesWritten, offset, buffer, bytesRead, 0))) { - break; - } - - offset += bytesRead; - } - - free(buffer); - - if(ciaHandle != 0) { - if(R_FAILED(res)) { - AM_CancelCIAInstall(ciaHandle); - } else if(R_SUCCEEDED(res = AM_FinishCiaInstall(ciaHandle))) { - if(titleId == 0x0004013800000002LL || titleId == 0x0004013820000002LL) { - res = AM_InstallFirm(titleId); - } - } - } - } else { - res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); - } - - cia_cancelled = false; - cia_result = res; - cia_installing = false; -} - -static void task_thread(void* arg) { - while(true) { - s32 index = 0; - svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX); - if(index == EVENT_QUIT) { - task_clear_files(); - task_clear_titles(); - task_clear_pending_titles(); - task_clear_tickets(); - - break; - } else if(index == EVENT_REFRESH_FILES) { - task_clear_files(); - - Result res = 0; - if(R_FAILED(res = task_load_files())) { - error_display_res(NULL, NULL, res, "Failed to load file listing."); - } - } else if(index == EVENT_REFRESH_TITLES) { - task_clear_titles(); - - Result res = 0; - if(R_FAILED(res = task_load_titles(MEDIATYPE_GAME_CARD)) || R_FAILED(res = task_load_titles(MEDIATYPE_SD)) || R_FAILED(res = task_load_titles(MEDIATYPE_NAND))) { - error_display_res(NULL, NULL, res, "Failed to load title listing."); - } - } else if(index == EVENT_REFRESH_PENDING_TITLES) { - task_clear_pending_titles(); - - Result res = 0; - if(R_FAILED(res = task_load_pending_titles(MEDIATYPE_SD)) || R_FAILED(res = task_load_pending_titles(MEDIATYPE_NAND))) { - error_display_res(NULL, NULL, res, "Failed to load pending title listing."); - } - } else if(index == EVENT_REFRESH_TICKETS) { - task_clear_tickets(); - - Result res = 0; - if(R_FAILED(res = task_load_tickets())) { - error_display_res(NULL, NULL, res, "Failed to load ticket listing."); - } - } else if(index == EVENT_REFRESH_EXT_SAVE_DATA) { - task_clear_ext_save_data(); - - Result res = 0; - if(R_FAILED(res = task_load_ext_save_data(MEDIATYPE_SD)) || R_FAILED(res = task_load_ext_save_data(MEDIATYPE_NAND))) { - error_display_res(NULL, NULL, res, "Failed to load ext save data listing."); - } - } else if(index == EVENT_REFRESH_SYSTEM_SAVE_DATA) { - task_clear_system_save_data(); - - Result res = 0; - if(R_FAILED(res = task_load_system_save_data())) { - error_display_res(NULL, NULL, res, "Failed to load system save data listing."); - } - } else if(index == EVENT_INSTALL_CIA) { - task_install_cia(); - } - } -} - -void task_init() { - aptOpenSession(); - Result setCpuTimeRes = APT_SetAppCpuTimeLimit(30); - aptCloseSession(); - - if(R_FAILED(setCpuTimeRes)) { - util_panic("Failed to set syscore CPU time: %08lX", setCpuTimeRes); - return; - } - - for(int i = 0; i < EVENT_COUNT; i++) { - Result eventRes = svcCreateEvent(&events[i], 0); - if(R_FAILED(eventRes)) { - util_panic("Failed to create thread event: %08lX", eventRes); - return; - } - } - - Result mutexRes = svcCreateMutex(&mutex, false); - if(R_FAILED(mutexRes)) { - util_panic("Failed to create thread mutex: %08lX", mutexRes); - return; - } - - task_thread_ptr = threadCreate(task_thread, NULL, 0x10000, 0x18, 1, false); - if(task_thread_ptr == NULL) { - util_panic("Failed to create task thread."); - return; - } -} - -void task_exit() { - if(task_thread_ptr != NULL) { - svcSignalEvent(events[EVENT_QUIT]); - threadJoin(task_thread_ptr, U64_MAX); - - threadFree(task_thread_ptr); - task_thread_ptr = NULL; - } - - for(int i = 0; i < EVENT_COUNT; i++) { - if(events[i] != 0) { - svcCloseHandle(events[i]); - events[i] = 0; - } - } - - if(mutex != 0) { - svcReleaseMutex(mutex); - svcCloseHandle(mutex); - mutex = 0; - } - - files_archive = NULL; - files_path = NULL; - - files_init = false; - titles_init = false; - pending_titles_init = false; - tickets_init = false; - ext_save_data_init = false; - system_save_data_init = false; -} - -Handle task_get_mutex() { - return mutex; -} - -void task_refresh_files() { - svcSignalEvent(events[EVENT_REFRESH_FILES]); - files_init = true; -} - -void task_refresh_titles() { - svcSignalEvent(events[EVENT_REFRESH_TITLES]); - titles_init = true; -} - -void task_refresh_pending_titles() { - svcSignalEvent(events[EVENT_REFRESH_PENDING_TITLES]); - pending_titles_init = true; -} - -void task_refresh_tickets() { - svcSignalEvent(events[EVENT_REFRESH_TICKETS]); - tickets_init = true; -} - -void task_refresh_ext_save_data() { - svcSignalEvent(events[EVENT_REFRESH_EXT_SAVE_DATA]); - ext_save_data_init = true; -} - -void task_refresh_system_save_data() { - svcSignalEvent(events[EVENT_REFRESH_SYSTEM_SAVE_DATA]); - system_save_data_init = true; -} - -void task_request_cia_install(FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)) { - cia_cancelled = true; - while(task_is_cia_installing()) { - svcSleepThread(1000000); - } - - cia_cancelled = false; - cia_result = 0; - cia_errno = 0; - cia_dest = dest; - cia_size = size; - cia_data = data; - cia_read = read; - - svcSignalEvent(events[EVENT_INSTALL_CIA]); - - while(!task_is_cia_installing()) { - svcSleepThread(1000000); - } -} - -Result task_get_cia_install_result() { - return cia_result; -} - -int task_get_cia_install_errno() { - return cia_errno; -} - -bool task_is_cia_installing() { - return cia_installing; -} - -void task_cancel_cia_install() { - cia_cancelled = true; -} - -FS_Archive* task_get_files_archive() { - return files_archive; -} - -void task_set_files_archive(FS_Archive* archive) { - files_archive = archive; -} - -char* task_get_files_path() { - return files_path; -} - -void task_set_files_path(char* path) { - files_path = path; -} - -list_item* task_get_files() { - if(!files_init) { - task_refresh_files(); - } - - return files_list; -} - -u32* task_get_files_count() { - if(!files_init) { - task_refresh_files(); - } - - return &files_count; -} - -list_item* task_get_titles() { - if(!titles_init) { - task_refresh_titles(); - } - - return title_list; -} - -u32* task_get_title_count() { - if(!titles_init) { - task_refresh_titles(); - } - - return &title_count; -} - -list_item* task_get_pending_titles() { - if(!pending_titles_init) { - task_refresh_pending_titles(); - } - - return pending_title_list; -} - -u32* task_get_pending_title_count() { - if(!pending_titles_init) { - task_refresh_pending_titles(); - } - - return &pending_title_count; -} - -list_item* task_get_tickets() { - if(!tickets_init) { - task_refresh_tickets(); - } - - return ticket_list; -} - -u32* task_get_ticket_count() { - if(!tickets_init) { - task_refresh_tickets(); - } - - return &ticket_count; -} - -list_item* task_get_ext_save_data() { - if(!ext_save_data_init) { - task_refresh_ext_save_data(); - } - - return ext_save_data_list; -} - -u32* task_get_ext_save_data_count() { - if(!ext_save_data_init) { - task_refresh_ext_save_data(); - } - - return &ext_save_data_count; -} - -list_item* task_get_system_save_data() { - if(!system_save_data_init) { - task_refresh_system_save_data(); - } - - return system_save_data_list; -} - -u32* task_get_system_save_data_count() { - if(!system_save_data_init) { - task_refresh_system_save_data(); - } - - return &system_save_data_count; -} - -void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ext_save_data_info* info = (ext_save_data_info*) data; - - char buf[64]; - - if(info->hasSmdh) { - u32 smdhInfoBoxShadowWidth; - u32 smdhInfoBoxShadowHeight; - screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); - - float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; - float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); - - u32 smdhInfoBoxWidth; - u32 smdhInfoBoxHeight; - screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); - - float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; - float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); - - u32 smdhIconWidth; - u32 smdhIconHeight; - screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture); - - float smdhIconX = smdhInfoBoxX + 8; - float smdhIconY = smdhInfoBoxY + 8; - screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); - - float shortDescriptionHeight; - screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f); - - float longDescriptionHeight; - screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f); - - float publisherHeight; - screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f); - - float smdhTextX = smdhIconX + smdhIconWidth + 8; - - float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; - screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; - screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; - screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); - } - - snprintf(buf, 64, "Ext Save Data ID: %016llX", info->extSaveDataId); - - float saveDataIdWidth; - float saveDataIdHeight; - screen_get_string_size(&saveDataIdWidth, &saveDataIdHeight, buf, 0.5f, 0.5f); - - float saveDataIdX = x1 + (x2 - x1 - saveDataIdWidth) / 2; - float saveDataIdY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, saveDataIdX, saveDataIdY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Shared: %s", info->shared ? "Yes" : "No"); - - float sharedWidth; - float sharedHeight; - screen_get_string_size(&sharedWidth, &sharedHeight, buf, 0.5f, 0.5f); - - float sharedX = x1 + (x2 - x1 - sharedWidth) / 2; - float sharedY = saveDataIdY + saveDataIdHeight + 2; - screen_draw_string(buf, sharedX, sharedY, 0.5f, 0.5f, 0xFF000000, false); -} - -void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - file_info* info = (file_info*) data; - - char buf[64]; - - if(strlen(info->name) > 48) { - snprintf(buf, 64, "Name: %.45s...", info->name); - } else { - snprintf(buf, 64, "Name: %.48s", info->name); - } - - float nameWidth; - float nameHeight; - screen_get_string_size(&nameWidth, &nameHeight, buf, 0.5f, 0.5f); - - float nameX = x1 + (x2 - x1 - nameWidth) / 2; - float nameY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, nameX, nameY, 0.5f, 0.5f, 0xFF000000, false); - - if(!info->isDirectory) { - snprintf(buf, 64, "Size: %.2f MB", info->size / 1024.0 / 1024.0); - - float sizeWidth; - float sizeHeight; - screen_get_string_size(&sizeWidth, &sizeHeight, buf, 0.5f, 0.5f); - - float sizeX = x1 + (x2 - x1 - sizeWidth) / 2; - float sizeY = nameY + nameHeight + 2; - screen_draw_string(buf, sizeX, sizeY, 0.5f, 0.5f, 0xFF000000, false); - - if(info->isCia) { - if(info->ciaInfo.hasSmdh) { - u32 smdhInfoBoxShadowWidth; - u32 smdhInfoBoxShadowHeight; - screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); - - float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; - float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); - - u32 smdhInfoBoxWidth; - u32 smdhInfoBoxHeight; - screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); - - float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; - float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); - - u32 smdhIconWidth; - u32 smdhIconHeight; - screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->ciaInfo.smdhInfo.texture); - - float smdhIconX = smdhInfoBoxX + 8; - float smdhIconY = smdhInfoBoxY + 8; - screen_draw_texture(info->ciaInfo.smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); - - float shortDescriptionHeight; - screen_get_string_size(NULL, &shortDescriptionHeight, info->ciaInfo.smdhInfo.shortDescription, 0.5f, 0.5f); - - float longDescriptionHeight; - screen_get_string_size(NULL, &longDescriptionHeight, info->ciaInfo.smdhInfo.longDescription, 0.5f, 0.5f); - - float publisherHeight; - screen_get_string_size(NULL, &publisherHeight, info->ciaInfo.smdhInfo.publisher, 0.5f, 0.5f); - - float smdhTextX = smdhIconX + smdhIconWidth + 8; - - float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; - screen_draw_string(info->ciaInfo.smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; - screen_draw_string(info->ciaInfo.smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; - screen_draw_string(info->ciaInfo.smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); - } - - snprintf(buf, 64, "Title ID: %016llX", info->ciaInfo.titleId); - - float titleIdWidth; - float titleIdHeight; - screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); - - float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; - float titleIdY = sizeY + sizeHeight + 2; - screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Version: %hu", info->ciaInfo.version); - - float versionWidth; - float versionHeight; - screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); - - float versionX = x1 + (x2 - x1 - versionWidth) / 2; - float versionY = titleIdY + titleIdHeight + 2; - screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Installed Size (SD): %.2f MB", info->ciaInfo.installedSizeSD / 1024.0 / 1024.0); - - float installedSizeSDWidth; - float installedSizeSDHeight; - screen_get_string_size(&installedSizeSDWidth, &installedSizeSDHeight, buf, 0.5f, 0.5f); - - float installedSizeSDX = x1 + (x2 - x1 - installedSizeSDWidth) / 2; - float installedSizeSDY = versionY + versionHeight + 2; - screen_draw_string(buf, installedSizeSDX, installedSizeSDY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Installed Size (NAND): %.2f MB", info->ciaInfo.installedSizeNAND / 1024.0 / 1024.0); - - float installedSizeNANDWidth; - float installedSizeNANDHeight; - screen_get_string_size(&installedSizeNANDWidth, &installedSizeNANDHeight, buf, 0.5f, 0.5f); - - float installedSizeNANDX = x1 + (x2 - x1 - installedSizeNANDWidth) / 2; - float installedSizeNANDY = installedSizeSDY + installedSizeSDHeight + 2; - screen_draw_string(buf, installedSizeNANDX, installedSizeNANDY, 0.5f, 0.5f, 0xFF000000, false); - } - } else { - snprintf(buf, 64, "Directory"); - - float directoryWidth; - float directoryHeight; - screen_get_string_size(&directoryWidth, &directoryHeight, buf, 0.5f, 0.5f); - - float directoryX = x1 + (x2 - x1 - directoryWidth) / 2; - float directoryY = nameY + nameHeight + 2; - screen_draw_string(buf, directoryX, directoryY, 0.5f, 0.5f, 0xFF000000, false); - } -} - -void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - system_save_data_info* info = (system_save_data_info*) data; - - char buf[64]; - - snprintf(buf, 64, "System Save Data ID: %016llX", info->systemSaveDataId); - - float saveDataIdWidth; - float saveDataIdHeight; - screen_get_string_size(&saveDataIdWidth, &saveDataIdHeight, buf, 0.5f, 0.5f); - - float saveDataIdX = x1 + (x2 - x1 - saveDataIdWidth) / 2; - float saveDataIdY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, saveDataIdX, saveDataIdY, 0.5f, 0.5f, 0xFF000000, false); -} - -void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - title_info* info = (title_info*) data; - - char buf[64]; - - if(info->hasSmdh) { - u32 smdhInfoBoxShadowWidth; - u32 smdhInfoBoxShadowHeight; - screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); - - float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; - float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); - - u32 smdhInfoBoxWidth; - u32 smdhInfoBoxHeight; - screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); - - float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; - float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; - screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); - - u32 smdhIconWidth; - u32 smdhIconHeight; - screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture); - - float smdhIconX = smdhInfoBoxX + 8; - float smdhIconY = smdhInfoBoxY + 8; - screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); - - float shortDescriptionHeight; - screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f); - - float longDescriptionHeight; - screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f); - - float publisherHeight; - screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f); - - float smdhTextX = smdhIconX + smdhIconWidth + 8; - - float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; - screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; - screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); - - float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; - screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); - } - - snprintf(buf, 64, "Title ID: %016llX", info->titleId); - - float titleIdWidth; - float titleIdHeight; - screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); - - float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; - float titleIdY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Media Type: %s", info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD" : "Game Card"); - - float mediaTypeWidth; - float mediaTypeHeight; - screen_get_string_size(&mediaTypeWidth, &mediaTypeHeight, buf, 0.5f, 0.5f); - - float mediaTypeX = x1 + (x2 - x1 - mediaTypeWidth) / 2; - float mediaTypeY = titleIdY + titleIdHeight + 2; - screen_draw_string(buf, mediaTypeX, mediaTypeY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Product Code: %s", info->productCode); - - float productCodeWidth; - float productCodeHeight; - screen_get_string_size(&productCodeWidth, &productCodeHeight, buf, 0.5f, 0.5f); - - float productCodeX = x1 + (x2 - x1 - productCodeWidth) / 2; - float productCodeY = mediaTypeY + mediaTypeHeight + 2; - screen_draw_string(buf, productCodeX, productCodeY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Version: %hu", info->version); - - float versionWidth; - float versionHeight; - screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); - - float versionX = x1 + (x2 - x1 - versionWidth) / 2; - float versionY = productCodeY + productCodeHeight + 2; - screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Installed Size: %.2f MB", info->installedSize / 1024.0 / 1024.0); - - float installedSizeWidth; - float installedSizeHeight; - screen_get_string_size(&installedSizeWidth, &installedSizeHeight, buf, 0.5f, 0.5f); - - float installedSizeX = x1 + (x2 - x1 - installedSizeWidth) / 2; - float installedSizeY = versionY + versionHeight + 2; - screen_draw_string(buf, installedSizeX, installedSizeY, 0.5f, 0.5f, 0xFF000000, false); -} - -void ui_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - pending_title_info* info = (pending_title_info*) data; - - char buf[64]; - - snprintf(buf, 64, "Pending Title ID: %016llX", info->titleId); - - float titleIdWidth; - float titleIdHeight; - screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); - - float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; - float titleIdY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Media Type: %s", info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD" : "Game Card"); - - float mediaTypeWidth; - float mediaTypeHeight; - screen_get_string_size(&mediaTypeWidth, &mediaTypeHeight, buf, 0.5f, 0.5f); - - float mediaTypeX = x1 + (x2 - x1 - mediaTypeWidth) / 2; - float mediaTypeY = titleIdY + titleIdHeight + 2; - screen_draw_string(buf, mediaTypeX, mediaTypeY, 0.5f, 0.5f, 0xFF000000, false); - - snprintf(buf, 64, "Version: %hu", info->version); - - float versionWidth; - float versionHeight; - screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); - - float versionX = x1 + (x2 - x1 - versionWidth) / 2; - float versionY = mediaTypeY + mediaTypeHeight + 2; - screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); -} - -void ui_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ticket_info* info = (ticket_info*) data; - - char buf[64]; - - snprintf(buf, 64, "Ticket ID: %016llX", info->ticketId); - - float titleIdWidth; - float titleIdHeight; - screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); - - float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; - float titleIdY = y1 + (y2 - y1) / 2 - 8; - screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); -} diff --git a/source/ui/section/task.h b/source/ui/section/task.h deleted file mode 100644 index a8aa148..0000000 --- a/source/ui/section/task.h +++ /dev/null @@ -1,114 +0,0 @@ -#pragma once - -#include - -#include "../list.h" - -#define CIA_INSTALL_RESULT_CANCELLED -1 -#define CIA_INSTALL_RESULT_ERRNO -2 -#define CIA_INSTALL_RESULT_WRONG_SYSTEM -3 - -typedef struct { - char shortDescription[0x81]; - char longDescription[0x161]; - char publisher[0x81]; - u32 texture; -} smdh_info; - -typedef struct { - u64 titleId; - u16 version; - u64 installedSizeSD; - u64 installedSizeNAND; - bool hasSmdh; - smdh_info smdhInfo; -} cia_info; - -typedef struct { - FS_Archive* archive; - char name[NAME_MAX]; - char path[PATH_MAX]; - bool isDirectory; - bool containsCias; - u64 size; - bool isCia; - cia_info ciaInfo; -} file_info; - -typedef struct { - FS_MediaType mediaType; - u64 titleId; - char productCode[0x10]; - u16 version; - u64 installedSize; - bool hasSmdh; - smdh_info smdhInfo; -} title_info; - -typedef struct { - FS_MediaType mediaType; - u64 titleId; - u16 version; -} pending_title_info; - -typedef struct { - u64 ticketId; -} ticket_info; - -typedef struct { - FS_MediaType mediaType; - u64 extSaveDataId; - bool shared; - bool hasSmdh; - smdh_info smdhInfo; -} ext_save_data_info; - -typedef struct { - u64 systemSaveDataId; -} system_save_data_info; - -void task_init(); -void task_exit(); -Handle task_get_mutex(); - -void task_refresh_files(); -void task_refresh_titles(); -void task_refresh_pending_titles(); -void task_refresh_tickets(); -void task_refresh_ext_save_data(); -void task_refresh_system_save_data(); - -void task_request_cia_install(FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)); -Result task_get_cia_install_result(); -int task_get_cia_install_errno(); -bool task_is_cia_installing(); -void task_cancel_cia_install(); - -FS_Archive* task_get_files_archive(); -void task_set_files_archive(FS_Archive* archive); -char* task_get_files_path(); -void task_set_files_path(char* path); -list_item* task_get_files(); -u32* task_get_files_count(); - -list_item* task_get_titles(); -u32* task_get_title_count(); - -list_item* task_get_pending_titles(); -u32* task_get_pending_title_count(); - -list_item* task_get_tickets(); -u32* task_get_ticket_count(); - -list_item* task_get_ext_save_data(); -u32* task_get_ext_save_data_count(); - -list_item* task_get_system_save_data(); -u32* task_get_system_save_data_count(); - -void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void ui_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); -void ui_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); \ No newline at end of file diff --git a/source/ui/section/task/installcia.c b/source/ui/section/task/installcia.c new file mode 100644 index 0000000..66b2bab --- /dev/null +++ b/source/ui/section/task/installcia.c @@ -0,0 +1,165 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> +#include + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "task.h" + +typedef struct { + install_cia_result* result; + FS_MediaType dest; + u64 size; + void* data; + Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size); + + Handle cancelEvent; +} install_cia_data; + +#define bswap_64(x) \ +({ \ + uint64_t __x = (x); \ + ((uint64_t)( \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000000000ffULL) << 56) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000000000ff00ULL) << 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000000000ff0000ULL) << 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00000000ff000000ULL) << 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x000000ff00000000ULL) >> 8) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x0000ff0000000000ULL) >> 24) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0x00ff000000000000ULL) >> 40) | \ + (uint64_t)(((uint64_t)(__x) & (uint64_t)0xff00000000000000ULL) >> 56) )); \ +}) + +static u32 align(u32 offset, u32 alignment) { + return (offset + (alignment - 1)) & ~(alignment - 1); +} + +static void task_install_cia_thread(void* arg) { + install_cia_data* data = (install_cia_data*) arg; + + memset(data->result, 0, sizeof(install_cia_result)); + + u32 bufferSize = 1024 * 256; + u8* buffer = (u8*) calloc(1, bufferSize); + if(buffer != NULL) { + bool firstBlock = true; + u64 titleId = 0; + + Handle ciaHandle = 0; + + u32 bytesRead = 0; + u32 bytesWritten = 0; + u64 offset = 0; + while(offset < data->size) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + data->result->cancelled = true; + break; + } + + u32 readSize = bufferSize; + if(data->size - offset < readSize) { + readSize = (u32) (data->size - offset); + } + + Result readRes = 0; + if(R_FAILED(readRes = data->read(data->data, &bytesRead, buffer, readSize))) { + if(readRes == -1) { + data->result->ioerr = true; + data->result->ioerrno = errno; + } else { + data->result->result = readRes; + } + + break; + } + + if(firstBlock) { + firstBlock = false; + + u32 headerSize = *(u32*) &buffer[0x00]; + u32 certSize = *(u32*) &buffer[0x08]; + titleId = bswap_64(*(u64*) &buffer[align(headerSize, 64) + align(certSize, 64) + 0x1DC]); + + if((titleId >> 32) & 0x8000) { + data->dest = MEDIATYPE_NAND; + } + + u8 n3ds = false; + if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((titleId >> 28) & 0xF) == 2) { + data->result->wrongSystem = true; + break; + } + + if(R_FAILED(data->result->result = AM_StartCiaInstall(data->dest, &ciaHandle))) { + break; + } + } + + if(R_FAILED(data->result->result = FSFILE_Write(ciaHandle, &bytesWritten, offset, buffer, bytesRead, 0))) { + break; + } + + offset += bytesRead; + } + + free(buffer); + + if(ciaHandle != 0) { + if(R_FAILED(data->result->result)) { + AM_CancelCIAInstall(ciaHandle); + } else if(R_SUCCEEDED(data->result->result = AM_FinishCiaInstall(ciaHandle))) { + if(titleId == 0x0004013800000002LL || titleId == 0x0004013820000002LL) { + data->result->result = AM_InstallFirm(titleId); + } + } + } + } else { + data->result->result = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + + if(R_FAILED(data->result->result) || data->result->cancelled || data->result->ioerr || data->result->wrongSystem) { + data->result->failed = true; + } + + data->result->finished = true; + + svcCloseHandle(data->cancelEvent); + free(data); +} + +Handle task_install_cia(install_cia_result* result, FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)) { + if(result == NULL || size == 0 || read == NULL) { + return 0; + } + + install_cia_data* installData = (install_cia_data*) calloc(1, sizeof(install_cia_data)); + installData->result = result; + installData->dest = dest; + installData->size = size; + installData->data = data; + installData->read = read; + + Result eventRes = svcCreateEvent(&installData->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create CIA installation cancel event."); + + free(installData); + return 0; + } + + if(threadCreate(task_install_cia_thread, installData, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create CIA installation thread."); + + svcCloseHandle(installData->cancelEvent); + free(installData); + return 0; + } + + return installData->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listextsavedata.c b/source/ui/section/task/listextsavedata.c new file mode 100644 index 0000000..6245b36 --- /dev/null +++ b/source/ui/section/task/listextsavedata.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../screen.h" +#include "../../../util.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; +} populate_ext_save_data_data; + +static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data, FS_MediaType mediaType) { + Result res = 0; + + u32 extSaveDataCount = 0; + u64* extSaveDataIds = (u64*) calloc(data->max, sizeof(u64)); + if(extSaveDataIds != NULL) { + if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, data->max, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) { + qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), util_compare_u64); + + SMDH smdh; + for(u32 i = 0; i < extSaveDataCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) calloc(1, sizeof(ext_save_data_info)); + if(extSaveDataInfo != NULL) { + extSaveDataInfo->mediaType = mediaType; + extSaveDataInfo->extSaveDataId = extSaveDataIds[i]; + extSaveDataInfo->shared = mediaType == MEDIATYPE_NAND; + + list_item* item = &data->items[*data->count]; + + FS_ExtSaveDataInfo info = {.mediaType = mediaType, .saveId = extSaveDataIds[i]}; + u32 smdhBytesRead = 0; + if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) &smdh)) && smdhBytesRead == sizeof(SMDH)) { + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); + + utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1); + + extSaveDataInfo->hasSmdh = true; + utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->smdhInfo.shortDescription) - 1); + utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->smdhInfo.longDescription) - 1); + utf16_to_utf8((uint8_t*) extSaveDataInfo->smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->smdhInfo.publisher) - 1); + extSaveDataInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); + } else { + extSaveDataInfo->hasSmdh = false; + } + + bool empty = strlen(item->name) == 0; + if(!empty) { + empty = true; + + char* curr = item->name; + while(*curr) { + if(*curr != ' ') { + empty = false; + break; + } + + curr++; + } + } + + if(empty) { + snprintf(item->name, NAME_MAX, "%016llX", extSaveDataIds[i]); + } + + if(mediaType == MEDIATYPE_NAND) { + item->rgba = 0xFF0000FF; + } else if(mediaType == MEDIATYPE_SD) { + item->rgba = 0xFF00FF00; + } else if(mediaType == MEDIATYPE_GAME_CARD) { + item->rgba = 0xFFFF0000; + } + + item->data = extSaveDataInfo; + + (*data->count)++; + } + } + } + + free(extSaveDataIds); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + + return res; +} + +static void task_populate_ext_save_data_thread(void* arg) { + populate_ext_save_data_data* data = (populate_ext_save_data_data*) arg; + + Result res = 0; + if(R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_NAND))) { + error_display_res(NULL, NULL, res, "Failed to load ext save data listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_ext_save_data(list_item* items, u32* count) { + if(items == NULL || count == NULL || *count == 0) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) items[i].data; + if(extSaveDataInfo->hasSmdh) { + screen_unload_texture(extSaveDataInfo->smdhInfo.texture); + } + + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_ext_save_data(list_item* items, u32* count, u32 max) { + if(items == NULL || count == NULL || max == 0) { + return 0; + } + + task_clear_ext_save_data(items, count); + + populate_ext_save_data_data* data = (populate_ext_save_data_data*) calloc(1, sizeof(populate_ext_save_data_data)); + data->items = items; + data->count = count; + data->max = max; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create ext save data list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_ext_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create ext save data list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listfiles.c b/source/ui/section/task/listfiles.c new file mode 100644 index 0000000..1d6d12f --- /dev/null +++ b/source/ui/section/task/listfiles.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "../../../screen.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; + + FS_Archive* archive; + const char* path; +} populate_files_data; + +static void task_populate_files_thread(void* arg) { + populate_files_data* data = (populate_files_data*) arg; + + Result res = 0; + + if(data->max > *data->count) { + file_info* dotFileInfo = (file_info*) calloc(1, sizeof(file_info)); + if(dotFileInfo != NULL) { + dotFileInfo->archive = data->archive; + strncpy(dotFileInfo->path, data->path, PATH_MAX); + util_get_path_file(dotFileInfo->name, dotFileInfo->path, NAME_MAX); + dotFileInfo->isDirectory = true; + dotFileInfo->containsCias = false; + dotFileInfo->size = 0; + dotFileInfo->isCia = false; + + list_item* dotItem = &data->items[*data->count]; + strncpy(dotItem->name, ".", NAME_MAX); + dotItem->rgba = 0xFF0000FF; + dotItem->data = dotFileInfo; + + (*data->count)++; + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + if(R_SUCCEEDED(res) && data->max > *data->count) { + file_info* dotDotFileInfo = (file_info*) calloc(1, sizeof(file_info)); + if(dotDotFileInfo != NULL) { + dotDotFileInfo->archive = data->archive; + util_get_parent_path(dotDotFileInfo->path, data->path, PATH_MAX); + util_get_path_file(dotDotFileInfo->name, dotDotFileInfo->path, NAME_MAX); + dotDotFileInfo->isDirectory = true; + dotDotFileInfo->containsCias = false; + dotDotFileInfo->size = 0; + dotDotFileInfo->isCia = false; + + list_item* dotDotItem = &data->items[*data->count]; + strncpy(dotDotItem->name, "..", NAME_MAX); + dotDotItem->rgba = 0xFF0000FF; + dotDotItem->data = dotDotFileInfo; + + (*data->count)++; + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + if(R_SUCCEEDED(res) && data->max > *data->count) { + Handle dirHandle = 0; + if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, *data->archive, fsMakePath(PATH_ASCII, data->path)))) { + u32 entryCount = 0; + FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(data->max, sizeof(FS_DirectoryEntry)); + if(entries != NULL) { + if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, data->max, entries)) && entryCount > 0) { + qsort(entries, entryCount, sizeof(FS_DirectoryEntry), util_compare_directory_entries); + + SMDH smdh; + for(u32 i = 0; i < entryCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + if(entries[i].attributes & FS_ATTRIBUTE_HIDDEN) { + continue; + } + + file_info* fileInfo = (file_info*) calloc(1, sizeof(file_info)); + if(fileInfo != NULL) { + char entryName[0x213] = {'\0'}; + utf16_to_utf8((uint8_t*) entryName, entries[i].name, sizeof(entryName) - 1); + + fileInfo->archive = data->archive; + strncpy(fileInfo->name, entryName, NAME_MAX); + + list_item* item = &data->items[*data->count]; + + if(entries[i].attributes & FS_ATTRIBUTE_DIRECTORY) { + item->rgba = 0xFF0000FF; + + snprintf(fileInfo->path, PATH_MAX, "%s%s/", data->path, entryName); + fileInfo->isDirectory = true; + fileInfo->containsCias = false; + fileInfo->size = 0; + fileInfo->isCia = false; + } else { + item->rgba = 0xFF000000; + + snprintf(fileInfo->path, PATH_MAX, "%s%s", data->path, entryName); + fileInfo->isDirectory = false; + fileInfo->containsCias = false; + fileInfo->size = 0; + fileInfo->isCia = false; + + Handle fileHandle; + if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *data->archive, fsMakePath(PATH_ASCII, fileInfo->path), FS_OPEN_READ, 0))) { + FSFILE_GetSize(fileHandle, &fileInfo->size); + + AM_TitleEntry titleEntry; + if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) { + fileInfo->containsCias = true; + + fileInfo->isCia = true; + fileInfo->ciaInfo.titleId = titleEntry.titleID; + fileInfo->ciaInfo.version = titleEntry.version; + fileInfo->ciaInfo.installedSizeSD = titleEntry.size; + fileInfo->ciaInfo.hasSmdh = false; + + if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) { + fileInfo->ciaInfo.installedSizeNAND = titleEntry.size; + } else { + fileInfo->ciaInfo.installedSizeNAND = 0; + } + + u32 bytesRead; + if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, fileInfo->size - sizeof(SMDH), &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { + if(smdh.magic[0] == 'S' && smdh.magic[1] == 'M' && smdh.magic[2] == 'D' && + smdh.magic[3] == 'H') { + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); + + fileInfo->ciaInfo.hasSmdh = true; + utf16_to_utf8((uint8_t *) fileInfo->ciaInfo.smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.smdhInfo.shortDescription) - 1); + utf16_to_utf8((uint8_t *) fileInfo->ciaInfo.smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.smdhInfo.longDescription) - 1); + utf16_to_utf8((uint8_t *) fileInfo->ciaInfo.smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.smdhInfo.publisher) - 1); + fileInfo->ciaInfo.smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); + } + } + } + + FSFILE_Close(fileHandle); + } + } + + strncpy(item->name, entryName, NAME_MAX); + item->data = fileInfo; + + (*data->count)++; + } + } + } + + free(entries); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + + FSDIR_Close(dirHandle); + } + } + + if(R_FAILED(res)) { + error_display_res(NULL, NULL, res, "Failed to load file listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_files(list_item* items, u32* count) { + if(items == NULL || count == NULL) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + file_info* fileInfo = (file_info*) items[i].data; + if(fileInfo->isCia && fileInfo->ciaInfo.hasSmdh) { + screen_unload_texture(fileInfo->ciaInfo.smdhInfo.texture); + } + + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_files(list_item* items, u32* count, u32 max, FS_Archive* archive, const char* path) { + if(items == NULL || count == NULL || max == 0 || archive == NULL || path == NULL) { + return 0; + } + + task_clear_files(items, count); + + populate_files_data* data = (populate_files_data*) calloc(1, sizeof(populate_files_data)); + data->items = items; + data->count = count; + data->max = max; + data->archive = archive; + data->path = path; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create file list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_files_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create file list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listpendingtitles.c b/source/ui/section/task/listpendingtitles.c new file mode 100644 index 0000000..829fc07 --- /dev/null +++ b/source/ui/section/task/listpendingtitles.c @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; +} populate_pending_titles_data; + +static Result task_populate_pending_titles_from(populate_pending_titles_data* data, FS_MediaType mediaType) { + Result res = 0; + + u32 pendingTitleCount = 0; + if(R_SUCCEEDED(res = AM_GetPendingTitleCount(&pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION))) { + u64* pendingTitleIds = (u64*) calloc(pendingTitleCount, sizeof(u64)); + if(pendingTitleIds != NULL) { + if(R_SUCCEEDED(res = AM_GetPendingTitleList(&pendingTitleCount, pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, pendingTitleIds))) { + qsort(pendingTitleIds, pendingTitleCount, sizeof(u64), util_compare_u64); + + AM_PendingTitleEntry* pendingTitleInfos = (AM_PendingTitleEntry*) calloc(pendingTitleCount, sizeof(AM_PendingTitleEntry)); + if(pendingTitleInfos != NULL) { + if(R_SUCCEEDED(res = AM_GetPendingTitleInfo(pendingTitleCount, mediaType, pendingTitleIds, pendingTitleInfos))) { + for(u32 i = 0; i < pendingTitleCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + pending_title_info* pendingTitleInfo = (pending_title_info*) calloc(1, sizeof(pending_title_info)); + if(pendingTitleInfo != NULL) { + pendingTitleInfo->mediaType = mediaType; + pendingTitleInfo->titleId = pendingTitleIds[i]; + pendingTitleInfo->version = pendingTitleInfos[i].version; + + list_item* item = &data->items[*data->count]; + snprintf(item->name, NAME_MAX, "%016llX", pendingTitleIds[i]); + if(mediaType == MEDIATYPE_NAND) { + item->rgba = 0xFF0000FF; + } else if(mediaType == MEDIATYPE_SD) { + item->rgba = 0xFF00FF00; + } + + item->data = pendingTitleInfo; + + (*data->count)++; + } + } + } + + free(pendingTitleInfos); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + free(pendingTitleIds); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + return res; +} + +static void task_populate_pending_titles_thread(void* arg) { + populate_pending_titles_data* data = (populate_pending_titles_data*) arg; + + Result res = 0; + if(R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_NAND))) { + error_display_res(NULL, NULL, res, "Failed to load pending title listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_pending_titles(list_item* items, u32* count) { + if(items == NULL || count == NULL) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_pending_titles(list_item* items, u32* count, u32 max) { + if(items == NULL || count == NULL || max == 0) { + return 0; + } + + task_clear_pending_titles(items, count); + + populate_pending_titles_data* data = (populate_pending_titles_data*) calloc(1, sizeof(populate_pending_titles_data)); + data->items = items; + data->count = count; + data->max = max; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create pending title list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_pending_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create pending title list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listsystemsavedata.c b/source/ui/section/task/listsystemsavedata.c new file mode 100644 index 0000000..a554120 --- /dev/null +++ b/source/ui/section/task/listsystemsavedata.c @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; +} populate_system_save_data_data; + +static void task_populate_system_save_data_thread(void* arg) { + populate_system_save_data_data* data = (populate_system_save_data_data*) arg; + + Result res = 0; + + u32 systemSaveDataCount = 0; + u64* systemSaveDataIds = (u64*) calloc(data->max, sizeof(u64)); + if(systemSaveDataIds != NULL) { + if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, data->max * sizeof(u64), systemSaveDataIds))) { + qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u64), util_compare_u64); + + for(u32 i = 0; i < systemSaveDataCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + system_save_data_info* systemSaveDataInfo = (system_save_data_info*) calloc(1, sizeof(system_save_data_info)); + if(systemSaveDataInfo != NULL) { + systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i]; + + list_item* item = &data->items[*data->count]; + snprintf(item->name, NAME_MAX, "%016llX", systemSaveDataIds[i]); + item->rgba = 0xFF000000; + item->data = systemSaveDataInfo; + + (*data->count)++; + } + } + } + + free(systemSaveDataIds); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + + if(R_FAILED(res)) { + error_display_res(NULL, NULL, res, "Failed to load system save data listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_system_save_data(list_item* items, u32* count) { + if(items == NULL || count == NULL) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_system_save_data(list_item* items, u32* count, u32 max) { + if(items == NULL || count == NULL || max == 0) { + return 0; + } + + task_clear_system_save_data(items, count); + + populate_system_save_data_data* data = (populate_system_save_data_data*) calloc(1, sizeof(populate_system_save_data_data)); + data->items = items; + data->count = count; + data->max = max; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create system save data list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_system_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create system save data list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listtickets.c b/source/ui/section/task/listtickets.c new file mode 100644 index 0000000..f5985e3 --- /dev/null +++ b/source/ui/section/task/listtickets.c @@ -0,0 +1,115 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; +} populate_tickets_data; + +static void task_populate_tickets_thread(void* arg) { + populate_tickets_data* data = (populate_tickets_data*) arg; + + Result res = 0; + + u32 ticketCount = 0; + if(R_SUCCEEDED(res = AM_GetTicketCount(&ticketCount))) { + u64* ticketIds = (u64*) calloc(ticketCount, sizeof(u64)); + if(ticketIds != NULL) { + if(R_SUCCEEDED(res = AM_GetTicketList(&ticketCount, ticketCount, 0, ticketIds))) { + qsort(ticketIds, ticketCount, sizeof(u64), util_compare_u64); + + for(u32 i = 0; i < ticketCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + ticket_info* ticketInfo = (ticket_info*) calloc(1, sizeof(ticket_info)); + if(ticketInfo != NULL) { + ticketInfo->ticketId = ticketIds[i]; + + list_item* item = &data->items[*data->count]; + snprintf(item->name, NAME_MAX, "%016llX", ticketIds[i]); + item->rgba = 0xFF000000; + item->data = ticketInfo; + + (*data->count)++; + } + } + } + + free(ticketIds); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + if(R_FAILED(res)) { + error_display_res(NULL, NULL, res, "Failed to load ticket listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_tickets(list_item* items, u32* count) { + if(items == NULL || count == NULL) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_tickets(list_item* items, u32* count, u32 max) { + if(items == NULL || count == NULL || max == 0) { + return 0; + } + + task_clear_tickets(items, count); + + populate_tickets_data* data = (populate_tickets_data*) calloc(1, sizeof(populate_tickets_data)); + data->items = items; + data->count = count; + data->max = max; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create ticket list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_tickets_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create ticket list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c new file mode 100644 index 0000000..34b7087 --- /dev/null +++ b/source/ui/section/task/listtitles.c @@ -0,0 +1,196 @@ +#include +#include +#include +#include +#include + +#include <3ds.h> + +#include "../../list.h" +#include "../../error.h" +#include "../../../util.h" +#include "../../../screen.h" +#include "task.h" + +typedef struct { + list_item* items; + u32* count; + u32 max; + + Handle cancelEvent; +} populate_titles_data; + +static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType mediaType) { + if(mediaType == MEDIATYPE_GAME_CARD && R_FAILED(FSUSER_GetCardType(NULL))) { + return 0; + } + + Result res = 0; + + u32 titleCount = 0; + if(R_SUCCEEDED(res = AM_GetTitleCount(mediaType, &titleCount))) { + u64* titleIds = (u64*) calloc(titleCount, sizeof(u64)); + if(titleIds != NULL) { + if(R_SUCCEEDED(res = AM_GetTitleList(&titleCount, mediaType, titleCount, titleIds))) { + qsort(titleIds, titleCount, sizeof(u64), util_compare_u64); + + AM_TitleEntry* titleInfos = (AM_TitleEntry*) calloc(titleCount, sizeof(AM_TitleEntry)); + if(titleInfos != NULL) { + if(R_SUCCEEDED(res = AM_GetTitleInfo(mediaType, titleCount, titleIds, titleInfos))) { + SMDH smdh; + for(u32 i = 0; i < titleCount && i < data->max; i++) { + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } + + title_info* titleInfo = (title_info*) calloc(1, sizeof(title_info)); + if(titleInfo != NULL) { + titleInfo->mediaType = mediaType; + titleInfo->titleId = titleIds[i]; + AM_GetTitleProductCode(mediaType, titleIds[i], titleInfo->productCode); + titleInfo->version = titleInfos[i].version; + titleInfo->installedSize = titleInfos[i].size; + titleInfo->hasSmdh = false; + + list_item* item = &data->items[*data->count]; + + static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000}; + static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData}; + u32 archivePath[] = {(u32) (titleIds[i] & 0xFFFFFFFF), (u32) ((titleIds[i] >> 32) & 0xFFFFFFFF), mediaType, 0x00000000}; + FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}}; + Handle fileHandle; + if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) { + u32 bytesRead; + if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { + if(smdh.magic[0] == 'S' && smdh.magic[1] == 'M' && smdh.magic[2] == 'D' && smdh.magic[3] == 'H') { + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); + + utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1); + + titleInfo->hasSmdh = true; + utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(titleInfo->smdhInfo.shortDescription) - 1); + utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(titleInfo->smdhInfo.longDescription) - 1); + utf16_to_utf8((uint8_t*) titleInfo->smdhInfo.publisher, smdh.titles[systemLanguage].publisher, sizeof(titleInfo->smdhInfo.publisher) - 1); + titleInfo->smdhInfo.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); + } + } + + FSFILE_Close(fileHandle); + } + + bool empty = strlen(item->name) == 0; + if(!empty) { + empty = true; + + char* curr = item->name; + while(*curr) { + if(*curr != ' ') { + empty = false; + break; + } + + curr++; + } + } + + if(empty) { + snprintf(item->name, NAME_MAX, "%016llX", titleIds[i]); + } + + if(mediaType == MEDIATYPE_NAND) { + item->rgba = 0xFF0000FF; + } else if(mediaType == MEDIATYPE_SD) { + item->rgba = 0xFF00FF00; + } else if(mediaType == MEDIATYPE_GAME_CARD) { + item->rgba = 0xFFFF0000; + } + + item->data = titleInfo; + + (*data->count)++; + } + } + } + + free(titleInfos); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + free(titleIds); + } else { + res = MAKERESULT(RL_PERMANENT, RS_INVALIDSTATE, 254, RD_OUT_OF_MEMORY); + } + } + + return res; +} + +static void task_populate_titles_thread(void* arg) { + populate_titles_data* data = (populate_titles_data*) arg; + + Result res = 0; + if(R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_GAME_CARD)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_NAND))) { + error_display_res(NULL, NULL, res, "Failed to load title listing."); + } + + svcCloseHandle(data->cancelEvent); + free(data); +} + +static void task_clear_titles(list_item* items, u32* count) { + if(items == NULL || count == NULL) { + return; + } + + u32 prevCount = *count; + *count = 0; + + for(u32 i = 0; i < prevCount; i++) { + if(items[i].data != NULL) { + title_info* titleInfo = (title_info*) items[i].data; + if(titleInfo->hasSmdh) { + screen_unload_texture(titleInfo->smdhInfo.texture); + } + + free(items[i].data); + items[i].data = NULL; + } + + memset(items[i].name, '\0', NAME_MAX); + items[i].rgba = 0; + } +} + +Handle task_populate_titles(list_item* items, u32* count, u32 max) { + if(items == NULL || count == NULL || max == 0) { + return 0; + } + + task_clear_titles(items, count); + + populate_titles_data* data = (populate_titles_data*) calloc(1, sizeof(populate_titles_data)); + data->items = items; + data->count = count; + data->max = max; + + Result eventRes = svcCreateEvent(&data->cancelEvent, 1); + if(R_FAILED(eventRes)) { + error_display_res(NULL, NULL, eventRes, "Failed to create title list cancel event."); + + free(data); + return 0; + } + + if(threadCreate(task_populate_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { + error_display(NULL, NULL, "Failed to create title list thread."); + + svcCloseHandle(data->cancelEvent); + free(data); + return 0; + } + + return data->cancelEvent; +} \ No newline at end of file diff --git a/source/ui/section/task/task.c b/source/ui/section/task/task.c new file mode 100644 index 0000000..e7b0808 --- /dev/null +++ b/source/ui/section/task/task.c @@ -0,0 +1,13 @@ +#include <3ds.h> + +#include "task.h" + +static bool task_quit; + +bool task_is_quit_all() { + return task_quit; +} + +void task_quit_all() { + task_quit = true; +} \ No newline at end of file diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h new file mode 100644 index 0000000..eef4966 --- /dev/null +++ b/source/ui/section/task/task.h @@ -0,0 +1,86 @@ +#pragma once + +#include + +#include "../../list.h" + +typedef struct { + char shortDescription[0x81]; + char longDescription[0x161]; + char publisher[0x81]; + u32 texture; +} smdh_info; + +typedef struct { + u64 titleId; + u16 version; + u64 installedSizeSD; + u64 installedSizeNAND; + bool hasSmdh; + smdh_info smdhInfo; +} cia_info; + +typedef struct { + FS_Archive* archive; + char name[NAME_MAX]; + char path[PATH_MAX]; + bool isDirectory; + bool containsCias; + u64 size; + bool isCia; + cia_info ciaInfo; +} file_info; + +typedef struct { + FS_MediaType mediaType; + u64 titleId; + char productCode[0x10]; + u16 version; + u64 installedSize; + bool hasSmdh; + smdh_info smdhInfo; +} title_info; + +typedef struct { + FS_MediaType mediaType; + u64 titleId; + u16 version; +} pending_title_info; + +typedef struct { + u64 ticketId; +} ticket_info; + +typedef struct { + FS_MediaType mediaType; + u64 extSaveDataId; + bool shared; + bool hasSmdh; + smdh_info smdhInfo; +} ext_save_data_info; + +typedef struct { + u64 systemSaveDataId; +} system_save_data_info; + +typedef struct { + bool finished; + bool failed; + bool cancelled; + bool ioerr; + bool wrongSystem; + + Result result; + int ioerrno; +} install_cia_result; + +bool task_is_quit_all(); +void task_quit_all(); + +Handle task_populate_ext_save_data(list_item* items, u32* count, u32 max); +Handle task_populate_files(list_item* items, u32* count, u32 max, FS_Archive* archive, const char* path); +Handle task_populate_pending_titles(list_item* items, u32* count, u32 max); +Handle task_populate_system_save_data(list_item* items, u32* count, u32 max); +Handle task_populate_tickets(list_item* items, u32* count, u32 max); +Handle task_populate_titles(list_item* items, u32* count, u32 max); +Handle task_install_cia(install_cia_result* result, FS_MediaType dest, u64 size, void* data, Result (*read)(void* data, u32* bytesRead, void* buffer, u32 size)); \ No newline at end of file diff --git a/source/ui/section/tickets.c b/source/ui/section/tickets.c index c3859fa..23ebee4 100644 --- a/source/ui/section/tickets.c +++ b/source/ui/section/tickets.c @@ -4,8 +4,18 @@ #include <3ds.h> #include "action/action.h" +#include "task/task.h" #include "section.h" +#define TICKETS_MAX 1024 + +typedef struct { + list_item items[TICKETS_MAX]; + u32 count; + Handle cancelEvent; + bool populated; +} tickets_data; + #define TICKETS_ACTION_COUNT 1 static u32 tickets_action_count = TICKETS_ACTION_COUNT; @@ -51,26 +61,44 @@ static void tickets_draw_top(ui_view* view, void* data, float x1, float y1, floa } static void tickets_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { + tickets_data* listData = (tickets_data*) data; + if(hidKeysDown() & KEY_B) { - list_destroy(view); ui_pop(); + free(listData); + list_destroy(view); return; } - if(hidKeysDown() & KEY_X) { - task_refresh_tickets(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + listData->cancelEvent = task_populate_tickets(listData->items, &listData->count, TICKETS_MAX); + listData->populated = true; } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + listData->populated = false; + ui_push(tickets_action_create((ticket_info*) selected->data)); + return; } - if(*itemCount != task_get_ticket_count() || *items != task_get_tickets()) { - *itemCount = task_get_ticket_count(); - *items = task_get_tickets(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void tickets_open() { - ui_push(list_create("Tickets", "A: Select, B: Return, X: Refresh", NULL, tickets_update, tickets_draw_top)); + tickets_data* data = (tickets_data*) calloc(1, sizeof(tickets_data)); + + ui_push(list_create("Tickets", "A: Select, B: Return, X: Refresh", data, tickets_update, tickets_draw_top)); } \ No newline at end of file diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index 4adfa90..bc0af42 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -4,8 +4,18 @@ #include <3ds.h> #include "action/action.h" +#include "task/task.h" #include "section.h" +#define TITLES_MAX 1024 + +typedef struct { + list_item items[TITLES_MAX]; + u32 count; + Handle cancelEvent; + bool populated; +} titles_data; + #define TITLES_ACTION_COUNT 5 static u32 titles_action_count = TITLES_ACTION_COUNT; @@ -55,26 +65,44 @@ static void titles_draw_top(ui_view* view, void* data, float x1, float y1, float } static void titles_update(ui_view* view, void* data, list_item** items, u32** itemCount, list_item* selected, bool selectedTouched) { + titles_data* listData = (titles_data*) data; + if(hidKeysDown() & KEY_B) { - list_destroy(view); ui_pop(); + free(listData); + list_destroy(view); return; } - if(hidKeysDown() & KEY_X) { - task_refresh_titles(); + if(!listData->populated || (hidKeysDown() & KEY_X)) { + if(listData->cancelEvent != 0) { + svcSignalEvent(listData->cancelEvent); + while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) { + svcSleepThread(1000000); + } + + listData->cancelEvent = 0; + } + + listData->cancelEvent = task_populate_titles(listData->items, &listData->count, TITLES_MAX); + listData->populated = true; } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + listData->populated = false; + ui_push(titles_action_create((title_info*) selected->data)); + return; } - if(*itemCount != task_get_title_count() || *items != task_get_titles()) { - *itemCount = task_get_title_count(); - *items = task_get_titles(); + if(*itemCount != &listData->count || *items != listData->items) { + *itemCount = &listData->count; + *items = listData->items; } } void titles_open() { - ui_push(list_create("Titles", "A: Select, B: Return, X: Refresh", NULL, titles_update, titles_draw_top)); + titles_data* data = (titles_data*) calloc(1, sizeof(titles_data)); + + ui_push(list_create("Titles", "A: Select, B: Return, X: Refresh", data, titles_update, titles_draw_top)); } \ No newline at end of file diff --git a/source/ui/ui.c b/source/ui/ui.c index c57c334..99f38ee 100644 --- a/source/ui/ui.c +++ b/source/ui/ui.c @@ -2,9 +2,11 @@ #include #include <3ds.h> +#include -#include "ui.h" +#include "section/task/task.h" #include "../screen.h" +#include "ui.h" #define MAX_UI_VIEWS 16 @@ -215,3 +217,375 @@ void ui_draw() { screen_end_frame(); } } + +void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + ext_save_data_info* info = (ext_save_data_info*) data; + + char buf[64]; + + if(info->hasSmdh) { + u32 smdhInfoBoxShadowWidth; + u32 smdhInfoBoxShadowHeight; + screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); + + float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; + float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); + + u32 smdhInfoBoxWidth; + u32 smdhInfoBoxHeight; + screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); + + float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; + float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); + + u32 smdhIconWidth; + u32 smdhIconHeight; + screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture); + + float smdhIconX = smdhInfoBoxX + 8; + float smdhIconY = smdhInfoBoxY + 8; + screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); + + float shortDescriptionHeight; + screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f); + + float longDescriptionHeight; + screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f); + + float publisherHeight; + screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f); + + float smdhTextX = smdhIconX + smdhIconWidth + 8; + + float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; + screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; + screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; + screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); + } + + snprintf(buf, 64, "Ext Save Data ID: %016llX", info->extSaveDataId); + + float saveDataIdWidth; + float saveDataIdHeight; + screen_get_string_size(&saveDataIdWidth, &saveDataIdHeight, buf, 0.5f, 0.5f); + + float saveDataIdX = x1 + (x2 - x1 - saveDataIdWidth) / 2; + float saveDataIdY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, saveDataIdX, saveDataIdY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Shared: %s", info->shared ? "Yes" : "No"); + + float sharedWidth; + float sharedHeight; + screen_get_string_size(&sharedWidth, &sharedHeight, buf, 0.5f, 0.5f); + + float sharedX = x1 + (x2 - x1 - sharedWidth) / 2; + float sharedY = saveDataIdY + saveDataIdHeight + 2; + screen_draw_string(buf, sharedX, sharedY, 0.5f, 0.5f, 0xFF000000, false); +} + +void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + file_info* info = (file_info*) data; + + char buf[64]; + + if(strlen(info->name) > 48) { + snprintf(buf, 64, "Name: %.45s...", info->name); + } else { + snprintf(buf, 64, "Name: %.48s", info->name); + } + + float nameWidth; + float nameHeight; + screen_get_string_size(&nameWidth, &nameHeight, buf, 0.5f, 0.5f); + + float nameX = x1 + (x2 - x1 - nameWidth) / 2; + float nameY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, nameX, nameY, 0.5f, 0.5f, 0xFF000000, false); + + if(!info->isDirectory) { + snprintf(buf, 64, "Size: %.2f MB", info->size / 1024.0 / 1024.0); + + float sizeWidth; + float sizeHeight; + screen_get_string_size(&sizeWidth, &sizeHeight, buf, 0.5f, 0.5f); + + float sizeX = x1 + (x2 - x1 - sizeWidth) / 2; + float sizeY = nameY + nameHeight + 2; + screen_draw_string(buf, sizeX, sizeY, 0.5f, 0.5f, 0xFF000000, false); + + if(info->isCia) { + if(info->ciaInfo.hasSmdh) { + u32 smdhInfoBoxShadowWidth; + u32 smdhInfoBoxShadowHeight; + screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); + + float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; + float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); + + u32 smdhInfoBoxWidth; + u32 smdhInfoBoxHeight; + screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); + + float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; + float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); + + u32 smdhIconWidth; + u32 smdhIconHeight; + screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->ciaInfo.smdhInfo.texture); + + float smdhIconX = smdhInfoBoxX + 8; + float smdhIconY = smdhInfoBoxY + 8; + screen_draw_texture(info->ciaInfo.smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); + + float shortDescriptionHeight; + screen_get_string_size(NULL, &shortDescriptionHeight, info->ciaInfo.smdhInfo.shortDescription, 0.5f, 0.5f); + + float longDescriptionHeight; + screen_get_string_size(NULL, &longDescriptionHeight, info->ciaInfo.smdhInfo.longDescription, 0.5f, 0.5f); + + float publisherHeight; + screen_get_string_size(NULL, &publisherHeight, info->ciaInfo.smdhInfo.publisher, 0.5f, 0.5f); + + float smdhTextX = smdhIconX + smdhIconWidth + 8; + + float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; + screen_draw_string(info->ciaInfo.smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; + screen_draw_string(info->ciaInfo.smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; + screen_draw_string(info->ciaInfo.smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); + } + + snprintf(buf, 64, "Title ID: %016llX", info->ciaInfo.titleId); + + float titleIdWidth; + float titleIdHeight; + screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); + + float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; + float titleIdY = sizeY + sizeHeight + 2; + screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Version: %hu", info->ciaInfo.version); + + float versionWidth; + float versionHeight; + screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); + + float versionX = x1 + (x2 - x1 - versionWidth) / 2; + float versionY = titleIdY + titleIdHeight + 2; + screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Installed Size (SD): %.2f MB", info->ciaInfo.installedSizeSD / 1024.0 / 1024.0); + + float installedSizeSDWidth; + float installedSizeSDHeight; + screen_get_string_size(&installedSizeSDWidth, &installedSizeSDHeight, buf, 0.5f, 0.5f); + + float installedSizeSDX = x1 + (x2 - x1 - installedSizeSDWidth) / 2; + float installedSizeSDY = versionY + versionHeight + 2; + screen_draw_string(buf, installedSizeSDX, installedSizeSDY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Installed Size (NAND): %.2f MB", info->ciaInfo.installedSizeNAND / 1024.0 / 1024.0); + + float installedSizeNANDWidth; + float installedSizeNANDHeight; + screen_get_string_size(&installedSizeNANDWidth, &installedSizeNANDHeight, buf, 0.5f, 0.5f); + + float installedSizeNANDX = x1 + (x2 - x1 - installedSizeNANDWidth) / 2; + float installedSizeNANDY = installedSizeSDY + installedSizeSDHeight + 2; + screen_draw_string(buf, installedSizeNANDX, installedSizeNANDY, 0.5f, 0.5f, 0xFF000000, false); + } + } else { + snprintf(buf, 64, "Directory"); + + float directoryWidth; + float directoryHeight; + screen_get_string_size(&directoryWidth, &directoryHeight, buf, 0.5f, 0.5f); + + float directoryX = x1 + (x2 - x1 - directoryWidth) / 2; + float directoryY = nameY + nameHeight + 2; + screen_draw_string(buf, directoryX, directoryY, 0.5f, 0.5f, 0xFF000000, false); + } +} + +void ui_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + pending_title_info* info = (pending_title_info*) data; + + char buf[64]; + + snprintf(buf, 64, "Pending Title ID: %016llX", info->titleId); + + float titleIdWidth; + float titleIdHeight; + screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); + + float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; + float titleIdY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Media Type: %s", info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD" : "Game Card"); + + float mediaTypeWidth; + float mediaTypeHeight; + screen_get_string_size(&mediaTypeWidth, &mediaTypeHeight, buf, 0.5f, 0.5f); + + float mediaTypeX = x1 + (x2 - x1 - mediaTypeWidth) / 2; + float mediaTypeY = titleIdY + titleIdHeight + 2; + screen_draw_string(buf, mediaTypeX, mediaTypeY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Version: %hu", info->version); + + float versionWidth; + float versionHeight; + screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); + + float versionX = x1 + (x2 - x1 - versionWidth) / 2; + float versionY = mediaTypeY + mediaTypeHeight + 2; + screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); +} + +void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + system_save_data_info* info = (system_save_data_info*) data; + + char buf[64]; + + snprintf(buf, 64, "System Save Data ID: %016llX", info->systemSaveDataId); + + float saveDataIdWidth; + float saveDataIdHeight; + screen_get_string_size(&saveDataIdWidth, &saveDataIdHeight, buf, 0.5f, 0.5f); + + float saveDataIdX = x1 + (x2 - x1 - saveDataIdWidth) / 2; + float saveDataIdY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, saveDataIdX, saveDataIdY, 0.5f, 0.5f, 0xFF000000, false); +} + +void ui_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + ticket_info* info = (ticket_info*) data; + + char buf[64]; + + snprintf(buf, 64, "Ticket ID: %016llX", info->ticketId); + + float titleIdWidth; + float titleIdHeight; + screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); + + float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; + float titleIdY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); +} + +void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) { + title_info* info = (title_info*) data; + + char buf[64]; + + if(info->hasSmdh) { + u32 smdhInfoBoxShadowWidth; + u32 smdhInfoBoxShadowHeight; + screen_get_texture_size(&smdhInfoBoxShadowWidth, &smdhInfoBoxShadowHeight, TEXTURE_SMDH_INFO_BOX_SHADOW); + + float smdhInfoBoxShadowX = x1 + (x2 - x1 - smdhInfoBoxShadowWidth) / 2; + float smdhInfoBoxShadowY = y1 + (y2 - y1) / 4 - smdhInfoBoxShadowHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX_SHADOW, smdhInfoBoxShadowX, smdhInfoBoxShadowY, smdhInfoBoxShadowWidth, smdhInfoBoxShadowHeight); + + u32 smdhInfoBoxWidth; + u32 smdhInfoBoxHeight; + screen_get_texture_size(&smdhInfoBoxWidth, &smdhInfoBoxHeight, TEXTURE_SMDH_INFO_BOX); + + float smdhInfoBoxX = x1 + (x2 - x1 - smdhInfoBoxWidth) / 2; + float smdhInfoBoxY = y1 + (y2 - y1) / 4 - smdhInfoBoxHeight / 2; + screen_draw_texture(TEXTURE_SMDH_INFO_BOX, smdhInfoBoxX, smdhInfoBoxY, smdhInfoBoxWidth, smdhInfoBoxHeight); + + u32 smdhIconWidth; + u32 smdhIconHeight; + screen_get_texture_size(&smdhIconWidth, &smdhIconHeight, info->smdhInfo.texture); + + float smdhIconX = smdhInfoBoxX + 8; + float smdhIconY = smdhInfoBoxY + 8; + screen_draw_texture(info->smdhInfo.texture, smdhIconX, smdhIconY, smdhIconWidth, smdhIconHeight); + + float shortDescriptionHeight; + screen_get_string_size(NULL, &shortDescriptionHeight, info->smdhInfo.shortDescription, 0.5f, 0.5f); + + float longDescriptionHeight; + screen_get_string_size(NULL, &longDescriptionHeight, info->smdhInfo.longDescription, 0.5f, 0.5f); + + float publisherHeight; + screen_get_string_size(NULL, &publisherHeight, info->smdhInfo.publisher, 0.5f, 0.5f); + + float smdhTextX = smdhIconX + smdhIconWidth + 8; + + float smdhShortDescriptionY = smdhIconY + (smdhIconHeight - shortDescriptionHeight - 2 - longDescriptionHeight - 2 - publisherHeight) / 2; + screen_draw_string(info->smdhInfo.shortDescription, smdhTextX, smdhShortDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhLongDescriptionY = smdhShortDescriptionY + shortDescriptionHeight + 2; + screen_draw_string(info->smdhInfo.longDescription, smdhTextX, smdhLongDescriptionY, 0.5f, 0.5f, 0xFF000000, false); + + float smdhPublisherY = smdhLongDescriptionY + longDescriptionHeight + 2; + screen_draw_string(info->smdhInfo.publisher, smdhTextX, smdhPublisherY, 0.5f, 0.5f, 0xFF000000, false); + } + + snprintf(buf, 64, "Title ID: %016llX", info->titleId); + + float titleIdWidth; + float titleIdHeight; + screen_get_string_size(&titleIdWidth, &titleIdHeight, buf, 0.5f, 0.5f); + + float titleIdX = x1 + (x2 - x1 - titleIdWidth) / 2; + float titleIdY = y1 + (y2 - y1) / 2 - 8; + screen_draw_string(buf, titleIdX, titleIdY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Media Type: %s", info->mediaType == MEDIATYPE_NAND ? "NAND" : info->mediaType == MEDIATYPE_SD ? "SD" : "Game Card"); + + float mediaTypeWidth; + float mediaTypeHeight; + screen_get_string_size(&mediaTypeWidth, &mediaTypeHeight, buf, 0.5f, 0.5f); + + float mediaTypeX = x1 + (x2 - x1 - mediaTypeWidth) / 2; + float mediaTypeY = titleIdY + titleIdHeight + 2; + screen_draw_string(buf, mediaTypeX, mediaTypeY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Product Code: %s", info->productCode); + + float productCodeWidth; + float productCodeHeight; + screen_get_string_size(&productCodeWidth, &productCodeHeight, buf, 0.5f, 0.5f); + + float productCodeX = x1 + (x2 - x1 - productCodeWidth) / 2; + float productCodeY = mediaTypeY + mediaTypeHeight + 2; + screen_draw_string(buf, productCodeX, productCodeY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Version: %hu", info->version); + + float versionWidth; + float versionHeight; + screen_get_string_size(&versionWidth, &versionHeight, buf, 0.5f, 0.5f); + + float versionX = x1 + (x2 - x1 - versionWidth) / 2; + float versionY = productCodeY + productCodeHeight + 2; + screen_draw_string(buf, versionX, versionY, 0.5f, 0.5f, 0xFF000000, false); + + snprintf(buf, 64, "Installed Size: %.2f MB", info->installedSize / 1024.0 / 1024.0); + + float installedSizeWidth; + float installedSizeHeight; + screen_get_string_size(&installedSizeWidth, &installedSizeHeight, buf, 0.5f, 0.5f); + + float installedSizeX = x1 + (x2 - x1 - installedSizeWidth) / 2; + float installedSizeY = versionY + versionHeight + 2; + screen_draw_string(buf, installedSizeX, installedSizeY, 0.5f, 0.5f, 0xFF000000, false); +} diff --git a/source/ui/ui.h b/source/ui/ui.h index 0faddaf..7ddad0b 100644 --- a/source/ui/ui.h +++ b/source/ui/ui.h @@ -15,4 +15,11 @@ bool ui_push(ui_view* view); ui_view* ui_peek(); ui_view* ui_pop(); void ui_update(); -void ui_draw(); \ No newline at end of file +void ui_draw(); + +void ui_draw_ext_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); +void ui_draw_file_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); +void ui_draw_pending_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); +void ui_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); +void ui_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); +void ui_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2); \ No newline at end of file diff --git a/source/util.c b/source/util.c index 570f6e3..3aa4d6d 100644 --- a/source/util.c +++ b/source/util.c @@ -6,7 +6,7 @@ #include <3ds.h> #include "util.h" -#include "ui/section/task.h" +#include "ui/section/task/task.h" extern void cleanup(); @@ -402,4 +402,30 @@ Result util_ensure_dir(FS_Archive* archive, const char* path) { } return res; +} + +int util_compare_u64(const void* e1, const void* e2) { + u64 id1 = *(u64*) e1; + u64 id2 = *(u64*) e2; + + return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; +} + +int util_compare_directory_entries(const void* e1, const void* e2) { + FS_DirectoryEntry* ent1 = (FS_DirectoryEntry*) e1; + FS_DirectoryEntry* ent2 = (FS_DirectoryEntry*) e2; + + if((ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { + return -1; + } else if(!(ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && (ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) { + return 1; + } else { + char entryName1[0x213] = {'\0'}; + utf16_to_utf8((uint8_t*) entryName1, ent1->name, sizeof(entryName1) - 1); + + char entryName2[0x213] = {'\0'}; + utf16_to_utf8((uint8_t*) entryName2, ent2->name, sizeof(entryName2) - 1); + + return strcasecmp(entryName1, entryName2); + } } \ No newline at end of file diff --git a/source/util.h b/source/util.h index 20d9ab1..dac7d28 100644 --- a/source/util.h +++ b/source/util.h @@ -1,5 +1,30 @@ #pragma once +typedef struct { + u16 shortDescription[0x40]; + u16 longDescription[0x80]; + u16 publisher[0x40]; +} SMDH_title; + +typedef struct { + char magic[0x04]; + u16 version; + u16 reserved1; + SMDH_title titles[0x10]; + u8 ratings[0x10]; + u32 region; + u32 matchMakerId; + u64 matchMakerBitId; + u32 flags; + u16 eulaVersion; + u16 reserved; + u32 optimalBannerFrame; + u32 streetpassId; + u64 reserved2; + u8 smallIcon[0x480]; + u8 largeIcon[0x1200]; +} SMDH; + void util_panic(const char* s, ...); bool util_is_dir(FS_Archive* archive, const char* path); @@ -15,4 +40,7 @@ Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* ar void util_free_contents(char** contents, u32 count); void util_get_path_file(char* out, const char* path, u32 size); void util_get_parent_path(char* out, const char* path, u32 size); -Result util_ensure_dir(FS_Archive* archive, const char* path); \ No newline at end of file +Result util_ensure_dir(FS_Archive* archive, const char* path); + +int util_compare_u64(const void* e1, const void* e2); +int util_compare_directory_entries(const void* e1, const void* e2); \ No newline at end of file