Split tasks up.

This commit is contained in:
Steven Smith 2016-02-18 21:16:05 -08:00
parent 267d08fbda
commit c9513fb91d
38 changed files with 1992 additions and 1845 deletions

View File

@ -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();

View File

@ -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;

View File

@ -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, ...);

View File

@ -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),

View File

@ -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) {

View File

@ -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) {

View File

@ -1,6 +1,6 @@
#pragma once
#include "../task.h"
#include "../task/task.h"
void action_browse_ext_save_data(ext_save_data_info* info);

View File

@ -3,7 +3,6 @@
#include <string.h>
#include <3ds.h>
#include <3ds/services/fs.h>
#include "clipboard.h"

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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;
}
}
}

View File

@ -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);
}

View File

@ -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));
}

View File

@ -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, "/");

View File

@ -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;
}
}
}

View File

@ -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));
}

View File

@ -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

View File

@ -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);

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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;
}

View 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));

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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);
}
}

View File

@ -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);