mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
Split tasks up.
This commit is contained in:
parent
267d08fbda
commit
c9513fb91d
@ -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();
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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, ...);
|
@ -2,7 +2,6 @@
|
||||
#include <malloc.h>
|
||||
|
||||
#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),
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <3ds.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#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) {
|
||||
|
@ -1,7 +1,6 @@
|
||||
#include <3ds.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#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) {
|
||||
|
@ -1,6 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "../task.h"
|
||||
#include "../task/task.h"
|
||||
|
||||
void action_browse_ext_save_data(ext_save_data_info* info);
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <3ds/services/fs.h>
|
||||
|
||||
#include "clipboard.h"
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,6 @@
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
@ -4,16 +4,21 @@
|
||||
#include <string.h>
|
||||
|
||||
#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, "/");
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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));
|
||||
}
|
@ -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));
|
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -1,114 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
#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);
|
165
source/ui/section/task/installcia.c
Normal file
165
source/ui/section/task/installcia.c
Normal file
@ -0,0 +1,165 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <errno.h>
|
||||
|
||||
#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;
|
||||
}
|
170
source/ui/section/task/listextsavedata.c
Normal file
170
source/ui/section/task/listextsavedata.c
Normal file
@ -0,0 +1,170 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
241
source/ui/section/task/listfiles.c
Normal file
241
source/ui/section/task/listfiles.c
Normal file
@ -0,0 +1,241 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
136
source/ui/section/task/listpendingtitles.c
Normal file
136
source/ui/section/task/listpendingtitles.c
Normal file
@ -0,0 +1,136 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
113
source/ui/section/task/listsystemsavedata.c
Normal file
113
source/ui/section/task/listsystemsavedata.c
Normal file
@ -0,0 +1,113 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
115
source/ui/section/task/listtickets.c
Normal file
115
source/ui/section/task/listtickets.c
Normal file
@ -0,0 +1,115 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
196
source/ui/section/task/listtitles.c
Normal file
196
source/ui/section/task/listtitles.c
Normal file
@ -0,0 +1,196 @@
|
||||
#include <sys/syslimits.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#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;
|
||||
}
|
13
source/ui/section/task/task.c
Normal file
13
source/ui/section/task/task.c
Normal file
@ -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;
|
||||
}
|
86
source/ui/section/task/task.h
Normal file
86
source/ui/section/task/task.h
Normal file
@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
|
||||
#include <sys/syslimits.h>
|
||||
|
||||
#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));
|
@ -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));
|
||||
}
|
@ -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));
|
||||
}
|
376
source/ui/ui.c
376
source/ui/ui.c
@ -2,9 +2,11 @@
|
||||
#include <time.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
||||
|
@ -15,4 +15,11 @@ bool ui_push(ui_view* view);
|
||||
ui_view* ui_peek();
|
||||
ui_view* ui_pop();
|
||||
void ui_update();
|
||||
void ui_draw();
|
||||
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);
|
@ -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);
|
||||
}
|
||||
}
|
@ -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);
|
||||
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);
|
Loading…
x
Reference in New Issue
Block a user