diff --git a/source/default.v.pica b/source/core/default.v.pica similarity index 100% rename from source/default.v.pica rename to source/core/default.v.pica diff --git a/source/core/linkedlist.c b/source/core/linkedlist.c index 99b1e62..9fdb3e5 100644 --- a/source/core/linkedlist.c +++ b/source/core/linkedlist.c @@ -1,4 +1,5 @@ #include +#include #include "linkedlist.h" @@ -185,6 +186,26 @@ bool linked_list_remove_at(linked_list* list, unsigned int index) { return true; } +void linked_list_sort(linked_list* list, int (*compare)(const void* p1, const void* p2)) { + unsigned int count = list->size; + void* elements[count]; + + unsigned int i = 0; + linked_list_node* node = list->first; + while(node != NULL && i < count) { + elements[i++] = node->value; + node = node->next; + } + + linked_list_clear(list); + + qsort(elements, count, sizeof(void*), compare); + + for(unsigned int index = 0; index < count; index++) { + linked_list_add(list, elements[index]); + } +} + void linked_list_iterate(linked_list* list, linked_list_iter* iter) { iter->list = list; linked_list_iter_restart(iter); diff --git a/source/core/linkedlist.h b/source/core/linkedlist.h index e8fe55f..fc1ce22 100644 --- a/source/core/linkedlist.h +++ b/source/core/linkedlist.h @@ -31,6 +31,7 @@ bool linked_list_add(linked_list* list, void* value); bool linked_list_add_at(linked_list* list, unsigned int index, void* value); bool linked_list_remove(linked_list* list, void* value); bool linked_list_remove_at(linked_list* list, unsigned int index); +void linked_list_sort(linked_list* list, int (*compare)(const void* p1, const void* p2)); void linked_list_iterate(linked_list* list, linked_list_iter* iter); diff --git a/source/screen.c b/source/core/screen.c similarity index 95% rename from source/screen.c rename to source/core/screen.c index 6be0fc4..8dc14a9 100644 --- a/source/screen.c +++ b/source/core/screen.c @@ -6,7 +6,7 @@ #include <3ds.h> #include -#include "stb_image/stb_image.h" +#include "../stb_image/stb_image.h" #include "screen.h" #include "util.h" @@ -52,6 +52,22 @@ static struct { static C3D_Tex* glyphSheets; +static FILE* screen_open_resource(const char* path) { + u32 realPathSize = strlen(path) + 16; + char realPath[realPathSize]; + + snprintf(realPath, realPathSize, "sdmc:/fbitheme/%s", path); + FILE* fd = fopen(realPath, "rb"); + + if(fd != NULL) { + return fd; + } else { + snprintf(realPath, realPathSize, "romfs:/%s", path); + + return fopen(realPath, "rb"); + } +} + void screen_init() { if(!C3D_Init(C3D_DEFAULT_CMDBUF_SIZE * 4)) { util_panic("Failed to initialize the GPU."); @@ -150,7 +166,7 @@ void screen_init() { tex->param = GPU_TEXTURE_MAG_FILTER(GPU_LINEAR) | GPU_TEXTURE_MIN_FILTER(GPU_LINEAR) | GPU_TEXTURE_WRAP_S(GPU_CLAMP_TO_EDGE) | GPU_TEXTURE_WRAP_T(GPU_CLAMP_TO_EDGE); } - FILE* fd = util_open_resource("textcolor.cfg"); + FILE* fd = screen_open_resource("textcolor.cfg"); if(fd == NULL) { util_panic("Failed to open text color config: %s\n", strerror(errno)); return; @@ -259,18 +275,30 @@ void screen_exit() { } } +static u32 screen_next_pow_2(u32 i) { + i--; + i |= i >> 1; + i |= i >> 2; + i |= i >> 4; + i |= i >> 8; + i |= i >> 16; + i++; + + return i; +} + void screen_load_texture(u32 id, void* data, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) { if(id >= MAX_TEXTURES) { util_panic("Attempted to load buffer to invalid texture ID \"%lu\".", id); return; } - u32 pow2Width = util_next_pow_2(width); + u32 pow2Width = screen_next_pow_2(width); if(pow2Width < 64) { pow2Width = 64; } - u32 pow2Height = util_next_pow_2(height); + u32 pow2Height = screen_next_pow_2(height); if(pow2Height < 64) { pow2Height = 64; } @@ -345,7 +373,7 @@ void screen_load_texture_file(u32 id, const char* path, bool linearFilter) { return; } - FILE* fd = util_open_resource(path); + FILE* fd = screen_open_resource(path); if(fd == NULL) { util_panic("Failed to load PNG file \"%s\": %s", path, strerror(errno)); return; @@ -401,23 +429,27 @@ u32 screen_load_texture_file_auto(const char* path, bool linearFilter) { return (u32) id; } +static u32 screen_tiled_texture_index(u32 x, u32 y, u32 w, u32 h) { + return (((y >> 3) * (w >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)); +} + void screen_load_texture_tiled(u32 id, void* tiledData, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter) { if(id >= MAX_TEXTURES) { util_panic("Attempted to load tiled data to invalid texture ID \"%lu\".", id); return; } - u32 pixelSize = size / width / height; - u8* untiledData = (u8*) calloc(1, size); if(untiledData == NULL) { util_panic("Failed to allocate buffer for texture untiling."); return; } + u32 pixelSize = size / width / height; + for(u32 x = 0; x < width; x++) { for(u32 y = 0; y < height; y++) { - u32 tiledDataPos = util_tiled_texture_index(x, y, width, height) * pixelSize; + u32 tiledDataPos = screen_tiled_texture_index(x, y, width, height) * pixelSize; u32 untiledDataPos = (y * width + x) * pixelSize; for(u32 i = 0; i < pixelSize; i++) { diff --git a/source/screen.h b/source/core/screen.h similarity index 89% rename from source/screen.h rename to source/core/screen.h index e90f597..f791635 100644 --- a/source/screen.h +++ b/source/core/screen.h @@ -6,7 +6,7 @@ #define BOTTOM_SCREEN_WIDTH 320 #define BOTTOM_SCREEN_HEIGHT 240 -#define MAX_TEXTURES 1536 // Maximum number of 64x64 RGBA8 textures that can fit in 24MB of linear RAM. +#define MAX_TEXTURES 1024 #define TEXTURE_BOTTOM_SCREEN_BG 0 #define TEXTURE_BOTTOM_SCREEN_TOP_BAR 1 @@ -40,6 +40,8 @@ #define TEXTURE_WIFI_3 29 #define TEXTURE_AUTO_START 30 +#define NUM_COLORS 6 + #define COLOR_TEXT 0 #define COLOR_NAND 1 #define COLOR_SD 2 @@ -47,15 +49,6 @@ #define COLOR_DS_TITLE 4 #define COLOR_DIRECTORY 5 -#define NUM_COLORS 6 - -/*#define COLOR_TEXT 0xFF000000 -#define COLOR_NAND 0xFF0000FF -#define COLOR_SD 0xFF00FF00 -#define COLOR_GAME_CARD 0xFFFF0000 -#define COLOR_DS_TITLE 0xFF82004B -#define COLOR_DIRECTORY 0xFF0000FF */ - void screen_init(); void screen_exit(); void screen_load_texture(u32 id, void* data, u32 size, u32 width, u32 height, GPU_TEXCOLOR format, bool linearFilter); diff --git a/source/util.c b/source/core/util.c similarity index 93% rename from source/util.c rename to source/core/util.c index e6aec4b..3b3dc84 100644 --- a/source/util.c +++ b/source/core/util.c @@ -4,10 +4,9 @@ #include #include <3ds.h> -#include <3ds/services/fs.h> #include "util.h" -#include "ui/section/task/task.h" +#include "../ui/section/task/task.h" extern void cleanup(); @@ -125,6 +124,54 @@ void util_panic(const char* s, ...) { exit(1); } +FS_Path* util_make_path_utf8(const char* path) { + size_t len = strlen(path); + + u16* utf16 = (u16*) calloc(len + 1, sizeof(u16)); + if(utf16 == NULL) { + return NULL; + } + + ssize_t utf16Len = utf8_to_utf16(utf16, (const uint8_t*) path, len); + + FS_Path* fsPath = (FS_Path*) calloc(1, sizeof(FS_Path)); + if(fsPath == NULL) { + free(utf16); + return NULL; + } + + fsPath->type = PATH_UTF16; + fsPath->size = (utf16Len + 1) * sizeof(u16); + fsPath->data = utf16; + + return fsPath; +} + +void util_free_path_utf8(FS_Path* path) { + free((void*) path->data); + free(path); +} + +bool util_exists(FS_Archive* archive, const char* path) { + bool exists = false; + + FS_Path* fsPath = util_make_path_utf8(path); + if(path != NULL) { + Handle handle = 0; + if(R_SUCCEEDED(FSUSER_OpenFile(&handle, *archive, *fsPath, FS_OPEN_READ, 0))) { + FSFILE_Close(handle); + exists = true; + } else if(R_SUCCEEDED(FSUSER_OpenDirectory(&handle, *archive, *fsPath))) { + FSDIR_Close(handle); + exists = true; + } + + util_free_path_utf8(fsPath); + } + + return exists; +} + bool util_is_dir(FS_Archive* archive, const char* path) { Result res = 0; @@ -143,6 +190,95 @@ bool util_is_dir(FS_Archive* archive, const char* path) { return R_SUCCEEDED(res); } +Result util_ensure_dir(FS_Archive* archive, const char* path) { + Result res = 0; + + if(!util_is_dir(archive, path)) { + FS_Path* fsPath = util_make_path_utf8(path); + if(fsPath != NULL) { + FSUSER_DeleteFile(*archive, *fsPath); + res = FSUSER_CreateDirectory(*archive, *fsPath, 0); + + util_free_path_utf8(fsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + } + + return res; +} + +void util_get_path_file(char* out, const char* path, u32 size) { + const char* start = NULL; + const char* end = NULL; + const char* curr = path - 1; + while((curr = strchr(curr + 1, '/')) != NULL) { + start = end != NULL ? end : path; + end = curr; + } + + if(end != path + strlen(path) - 1) { + start = end; + end = path + strlen(path); + } + + if(end - start == 0) { + strncpy(out, "/", size); + } else { + u32 terminatorPos = end - start - 1 < size - 1 ? end - start - 1 : size - 1; + strncpy(out, start + 1, terminatorPos); + out[terminatorPos] = '\0'; + } +} + +void util_get_parent_path(char* out, const char* path, u32 size) { + size_t pathLen = strlen(path); + + const char* start = NULL; + const char* end = NULL; + const char* curr = path - 1; + while((curr = strchr(curr + 1, '/')) != NULL && (start == NULL || curr != path + pathLen - 1)) { + start = end != NULL ? end : path; + end = curr; + } + + u32 terminatorPos = end - path + 1 < size - 1 ? end - path + 1 : size - 1; + strncpy(out, path, terminatorPos); + out[terminatorPos] = '\0'; +} + +bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes) { + return (bool) (attributes & FS_ATTRIBUTE_DIRECTORY); +} + +bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes) { + return !(attributes & FS_ATTRIBUTE_DIRECTORY); +} + +bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes) { + return !(attributes & FS_ATTRIBUTE_HIDDEN); +} + +bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes) { + if(data == NULL) { + return true; + } + + char* extension = (char*) data; + size_t extensionLen = strlen(extension); + + size_t len = strlen(path); + return util_filter_files(data, archive, path, attributes) && len >= extensionLen && strcmp(path + len - extensionLen, extension) == 0; +} + +bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes) { + if(data == NULL) { + return true; + } + + return strcmp(path, (char*) data) != 0; +} + static Result util_traverse_dir_internal(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes), void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) { Result res = 0; @@ -152,19 +288,19 @@ static Result util_traverse_dir_internal(FS_Archive* archive, const char* path, Handle handle = 0; if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&handle, *archive, *fsPath))) { size_t pathLen = strlen(path); - char* pathBuf = (char*) calloc(1, PATH_MAX); + char* pathBuf = (char*) calloc(1, FILE_PATH_MAX); if(pathBuf != NULL) { - strncpy(pathBuf, path, PATH_MAX); + strncpy(pathBuf, path, FILE_PATH_MAX); u32 entryCount = 0; FS_DirectoryEntry entry; u32 done = 0; while(R_SUCCEEDED(FSDIR_Read(handle, &entryCount, 1, &entry)) && entryCount > 0) { - ssize_t units = utf16_to_utf8((uint8_t*) pathBuf + pathLen, entry.name, PATH_MAX - pathLen - 1); + ssize_t units = utf16_to_utf8((uint8_t*) pathBuf + pathLen, entry.name, FILE_PATH_MAX - pathLen - 1); if(units > 0) { pathBuf[pathLen + units] = '\0'; if(entry.attributes & FS_ATTRIBUTE_DIRECTORY) { - if(pathLen + units < PATH_MAX - 2) { + if(pathLen + units < FILE_PATH_MAX - 2) { pathBuf[pathLen + units] = '/'; pathBuf[pathLen + units + 1] = '\0'; } @@ -228,7 +364,7 @@ static Result util_traverse_dir(FS_Archive* archive, const char* path, bool recu } static Result util_traverse_file(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes), - void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) { + void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) { Result res = 0; FS_Path* fsPath = util_make_path_utf8(path); @@ -263,38 +399,6 @@ Result util_traverse_contents(FS_Archive* archive, const char* path, bool recurs return res; } -bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes) { - return (bool) (attributes & FS_ATTRIBUTE_DIRECTORY); -} - -bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes) { - return !(attributes & FS_ATTRIBUTE_DIRECTORY); -} - -bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes) { - return !(attributes & FS_ATTRIBUTE_HIDDEN); -} - -bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes) { - if(data == NULL) { - return true; - } - - char* extension = (char*) data; - size_t extensionLen = strlen(extension); - - size_t len = strlen(path); - return util_filter_files(data, archive, path, attributes) && len >= extensionLen && strcmp(path + len - extensionLen, extension) == 0; -} - -bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes) { - if(data == NULL) { - return true; - } - - return strcmp(path, (char*) data) != 0; -} - typedef struct { u32* count; void* data; @@ -391,86 +495,6 @@ void util_free_contents(char** contents, u32 count) { free(contents); } -void util_get_path_file(char* out, const char* path, u32 size) { - const char* start = NULL; - const char* end = NULL; - const char* curr = path - 1; - while((curr = strchr(curr + 1, '/')) != NULL) { - start = end != NULL ? end : path; - end = curr; - } - - if(end - start == 0) { - strncpy(out, "/", size); - } else { - u32 terminatorPos = end - start - 1 < size - 1 ? end - start - 1 : size - 1; - strncpy(out, start + 1, terminatorPos); - out[terminatorPos] = '\0'; - } -} - -void util_get_parent_path(char* out, const char* path, u32 size) { - size_t pathLen = strlen(path); - - const char* start = NULL; - const char* end = NULL; - const char* curr = path - 1; - while((curr = strchr(curr + 1, '/')) != NULL && (start == NULL || curr != path + pathLen - 1)) { - start = end != NULL ? end : path; - end = curr; - } - - u32 terminatorPos = end - path + 1 < size - 1 ? end - path + 1 : size - 1; - strncpy(out, path, terminatorPos); - out[terminatorPos] = '\0'; -} - -Result util_ensure_dir(FS_Archive* archive, const char* path) { - Result res = 0; - - if(!util_is_dir(archive, path)) { - FS_Path* fsPath = util_make_path_utf8(path); - if(fsPath != NULL) { - FSUSER_DeleteFile(*archive, *fsPath); - res = FSUSER_CreateDirectory(*archive, *fsPath, 0); - - util_free_path_utf8(fsPath); - } else { - res = R_FBI_OUT_OF_MEMORY; - } - } - - return res; -} - -FS_Path* util_make_path_utf8(const char* path) { - size_t len = strlen(path); - - u16* utf16 = (u16*) calloc(len + 1, sizeof(u16)); - if(utf16 == NULL) { - return NULL; - } - - ssize_t utf16Len = utf8_to_utf16(utf16, (const uint8_t*) path, len); - - FS_Path* fsPath = (FS_Path*) calloc(1, sizeof(FS_Path)); - if(fsPath == NULL) { - free(utf16); - return NULL; - } - - fsPath->type = PATH_UTF16; - fsPath->size = (utf16Len + 1) * sizeof(u16); - fsPath->data = utf16; - - return fsPath; -} - -void util_free_path_utf8(FS_Path* path) { - free((void*) path->data); - free(path); -} - int util_compare_u32(const void* e1, const void* e2) { u32 id1 = *(u32*) e1; u32 id2 = *(u32*) e2; @@ -502,36 +526,4 @@ int util_compare_directory_entries(const void* e1, const void* e2) { return strcasecmp(entryName1, entryName2); } -} - -u32 util_next_pow_2(u32 i) { - i--; - i |= i >> 1; - i |= i >> 2; - i |= i >> 4; - i |= i >> 8; - i |= i >> 16; - i++; - - return i; -} - -u32 util_tiled_texture_index(u32 x, u32 y, u32 w, u32 h) { - return (((y >> 3) * (w >> 3) + (x >> 3)) << 6) + ((x & 1) | ((y & 1) << 1) | ((x & 2) << 1) | ((y & 2) << 2) | ((x & 4) << 2) | ((y & 4) << 3)); -} - -FILE* util_open_resource(const char* path) { - u32 realPathSize = strlen(path) + 16; - char realPath[realPathSize]; - - snprintf(realPath, realPathSize, "sdmc:/fbitheme/%s", path); - FILE* fd = fopen(realPath, "rb"); - - if(fd != NULL) { - return fd; - } else { - snprintf(realPath, realPathSize, "romfs:/%s", path); - - return fopen(realPath, "rb"); - } } \ No newline at end of file diff --git a/source/util.h b/source/core/util.h similarity index 94% rename from source/util.h rename to source/core/util.h index 7b88d52..1a5077c 100644 --- a/source/util.h +++ b/source/core/util.h @@ -1,7 +1,5 @@ #pragma once -#include - typedef struct { u16 shortDescription[0x40]; u16 longDescription[0x80]; @@ -42,29 +40,28 @@ typedef struct { void util_panic(const char* s, ...); +FS_Path* util_make_path_utf8(const char* path); +void util_free_path_utf8(FS_Path* path); + +bool util_exists(FS_Archive* archive, const char* path); bool util_is_dir(FS_Archive* archive, const char* path); -Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes), - void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)); +Result util_ensure_dir(FS_Archive* archive, const char* path); + +void util_get_path_file(char* out, const char* path, u32 size); +void util_get_parent_path(char* out, const char* path, u32 size); + bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes); bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes); bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes); bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes); bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes); + +Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes), + void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)); Result util_count_contents(u32* out, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)); Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)); 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); - -FS_Path* util_make_path_utf8(const char* path); -void util_free_path_utf8(FS_Path* path); int util_compare_u32(const void* e1, const void* e2); int util_compare_u64(const void* e1, const void* e2); -int util_compare_directory_entries(const void* e1, const void* e2); - -u32 util_next_pow_2(u32 i); -u32 util_tiled_texture_index(u32 x, u32 y, u32 w, u32 h); - -FILE* util_open_resource(const char* path); \ No newline at end of file +int util_compare_directory_entries(const void* e1, const void* e2); \ No newline at end of file diff --git a/source/main.c b/source/main.c index 52cd8df..b563698 100644 --- a/source/main.c +++ b/source/main.c @@ -2,10 +2,11 @@ #include <3ds.h> -#include "screen.h" -#include "util.h" +#include "core/screen.h" +#include "core/util.h" #include "svchax/svchax.h" #include "ui/mainmenu.h" +#include "ui/ui.h" #include "ui/section/action/clipboard.h" #include "ui/section/task/task.h" @@ -14,8 +15,7 @@ static void* soc_buffer; void cleanup() { clipboard_clear(); - task_quit_all(); - + task_exit(); ui_exit(); screen_exit(); @@ -70,6 +70,7 @@ int main(int argc, const char* argv[]) { screen_init(); ui_init(); + task_init(); mainmenu_open(); diff --git a/source/ui/error.c b/source/ui/error.c index 77cae5f..72990f6 100644 --- a/source/ui/error.c +++ b/source/ui/error.c @@ -7,7 +7,7 @@ #include "error.h" #include "prompt.h" -#include "../screen.h" +#include "../core/screen.h" static const char* level_to_string(Result res) { switch(R_LEVEL(res)) { diff --git a/source/ui/error.h b/source/ui/error.h index fe1d3ee..2227ff5 100644 --- a/source/ui/error.h +++ b/source/ui/error.h @@ -1,6 +1,6 @@ #pragma once -#include "ui.h" +typedef struct ui_view_s ui_view; void error_display(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...); void error_display_res(volatile bool* dismissed, void* data, void (* drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), Result result, const char* text, ...); diff --git a/source/ui/info.c b/source/ui/info.c index bda178a..843010f 100644 --- a/source/ui/info.c +++ b/source/ui/info.c @@ -5,7 +5,8 @@ #include "error.h" #include "info.h" -#include "../screen.h" +#include "ui.h" +#include "../core/screen.h" typedef struct { bool bar; diff --git a/source/ui/info.h b/source/ui/info.h index 8e2f729..43d17b7 100644 --- a/source/ui/info.h +++ b/source/ui/info.h @@ -1,9 +1,9 @@ #pragma once -#include "ui.h" - #define PROGRESS_TEXT_MAX 512 +typedef struct ui_view_s ui_view; + void info_display(const char* name, const char* info, bool bar, void* data, void (*update)(ui_view* view, void* data, float* progress, char* text), void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2)); void info_destroy(ui_view* view); \ No newline at end of file diff --git a/source/ui/list.c b/source/ui/list.c index 0aa654e..d9e5a6b 100644 --- a/source/ui/list.c +++ b/source/ui/list.c @@ -4,7 +4,8 @@ #include "error.h" #include "list.h" -#include "../screen.h" +#include "ui.h" +#include "../core/screen.h" #include "../core/linkedlist.h" typedef struct { diff --git a/source/ui/list.h b/source/ui/list.h index f2f4a26..af620b6 100644 --- a/source/ui/list.h +++ b/source/ui/list.h @@ -1,12 +1,12 @@ #pragma once -#include +#define LIST_ITEM_NAME_MAX 512 -#include "ui.h" -#include "../core/linkedlist.h" +typedef struct linked_list_s linked_list; +typedef struct ui_view_s ui_view; -typedef struct { - char name[NAME_MAX]; +typedef struct list_item_s { + char name[LIST_ITEM_NAME_MAX]; u32 color; void* data; } list_item; diff --git a/source/ui/mainmenu.c b/source/ui/mainmenu.c index 0a11406..bbf4f52 100644 --- a/source/ui/mainmenu.c +++ b/source/ui/mainmenu.c @@ -5,8 +5,10 @@ #include "list.h" #include "mainmenu.h" +#include "ui.h" #include "section/section.h" -#include "../screen.h" +#include "../core/linkedlist.h" +#include "../core/screen.h" static list_item sd = {"SD", COLOR_TEXT, files_open_sd}; static list_item ctr_nand = {"CTR NAND", COLOR_TEXT, files_open_ctr_nand}; diff --git a/source/ui/mainmenu.h b/source/ui/mainmenu.h index 52f1e71..fbf3382 100644 --- a/source/ui/mainmenu.h +++ b/source/ui/mainmenu.h @@ -1,5 +1,3 @@ #pragma once -#include "ui.h" - void mainmenu_open(); diff --git a/source/ui/prompt.c b/source/ui/prompt.c index 1e903f4..0a0eba7 100644 --- a/source/ui/prompt.c +++ b/source/ui/prompt.c @@ -4,7 +4,8 @@ #include "error.h" #include "prompt.h" -#include "../screen.h" +#include "ui.h" +#include "../core/screen.h" typedef struct { const char* text; diff --git a/source/ui/prompt.h b/source/ui/prompt.h index 5206561..7e4e971 100644 --- a/source/ui/prompt.h +++ b/source/ui/prompt.h @@ -1,7 +1,7 @@ #pragma once -#include "ui.h" +typedef struct ui_view_s ui_view; void prompt_display(const char* name, const char* text, u32 rgba, bool option, void* data, void (*update)(ui_view* view, void* data), - void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), - void (*onResponse)(ui_view* view, void* data, bool response)); \ No newline at end of file + void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), + void (*onResponse)(ui_view* view, void* data, bool response)); \ No newline at end of file diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index a3c7554..a4ce259 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -1,34 +1,43 @@ #pragma once -#include "../task/task.h" +typedef struct file_info_s file_info; +typedef struct linked_list_s linked_list; +typedef struct list_item_s list_item; -void action_browse_boss_ext_save_data(ext_save_data_info* info, bool* populated); -void action_browse_user_ext_save_data(ext_save_data_info* , bool* populatedinfo); -void action_delete_ext_save_data(ext_save_data_info* info, bool* populated); +void action_browse_boss_ext_save_data(linked_list* items, list_item* selected); +void action_browse_user_ext_save_data(linked_list* items, list_item* selected); +void action_delete_ext_save_data(linked_list* items, list_item* selected); -void action_browse_system_save_data(system_save_data_info* info, bool* populated); -void action_delete_system_save_data(system_save_data_info* info, bool* populated); +void action_browse_system_save_data(linked_list* items, list_item* selected); +void action_delete_system_save_data(linked_list* items, list_item* selected); -void action_install_cias(file_info* info, bool* populated); -void action_install_cias_delete(file_info* info, bool* populated); -void action_install_tickets(file_info* info, bool* populated); -void action_copy_contents(file_info* info, bool* populated); -void action_delete_contents(file_info* info, bool* populated); -void action_delete_dir(file_info* info, bool* populated); -void action_delete_dir_contents(file_info* info, bool* populated); -void action_delete_dir_cias(file_info* info, bool* populated); -void action_paste_contents(file_info* info, bool* populated); +void action_install_cia(linked_list* items, list_item* selected, file_info* target); +void action_install_cia_delete(linked_list* items, list_item* selected, file_info* target); +void action_install_cias(linked_list* items, list_item* selected, file_info* target); +void action_install_cias_delete(linked_list* items, list_item* selected, file_info* target); +void action_install_ticket(linked_list* items, list_item* selected, file_info* target); +void action_install_ticket_delete(linked_list* items, list_item* selected, file_info* target); +void action_install_tickets(linked_list* items, list_item* selected, file_info* target); +void action_install_tickets_delete(linked_list* items, list_item* selected, file_info* target); +void action_copy_content(linked_list* items, list_item* selected, file_info* target); +void action_copy_contents(linked_list* items, list_item* selected, file_info* target); +void action_delete_contents(linked_list* items, list_item* selected, file_info* target); +void action_delete_dir(linked_list* items, list_item* selected, file_info* target); +void action_delete_dir_contents(linked_list* items, list_item* selected, file_info* target); +void action_delete_dir_cias(linked_list* items, list_item* selected, file_info* target); +void action_delete_dir_tickets(linked_list* items, list_item* selected, file_info* target); +void action_paste_contents(linked_list* items, list_item* selected, file_info* target); -void action_delete_pending_title(pending_title_info* info, bool* populated); -void action_delete_all_pending_titles(pending_title_info* info, bool* populated); +void action_delete_pending_title(linked_list* items, list_item* selected); +void action_delete_all_pending_titles(linked_list* items, list_item* selected); -void action_delete_ticket(ticket_info* info, bool* populated); -void action_install_cdn(ticket_info* info, bool* populated); +void action_delete_ticket(linked_list* items, list_item* selected); +void action_install_cdn(linked_list* items, list_item* selected); -void action_delete_title(title_info* info, bool* populated); -void action_launch_title(title_info* info, bool* populated); -void action_extract_smdh(title_info* info, bool* populated); -void action_browse_title_save_data(title_info* info, bool* populated); -void action_import_secure_value(title_info* info, bool* populated); -void action_export_secure_value(title_info* info, bool* populated); -void action_delete_secure_value(title_info* info, bool* populated); \ No newline at end of file +void action_delete_title(linked_list* items, list_item* selected); +void action_launch_title(linked_list* items, list_item* selected); +void action_extract_smdh(linked_list* items, list_item* selected); +void action_browse_title_save_data(linked_list* items, list_item* selected); +void action_import_secure_value(linked_list* items, list_item* selected); +void action_export_secure_value(linked_list* items, list_item* selected); +void action_delete_secure_value(linked_list* items, list_item* selected); \ No newline at end of file diff --git a/source/ui/section/action/browsebossextsavedata.c b/source/ui/section/action/browsebossextsavedata.c index da3b3c7..248f2a8 100644 --- a/source/ui/section/action/browsebossextsavedata.c +++ b/source/ui/section/action/browsebossextsavedata.c @@ -2,8 +2,12 @@ #include "action.h" #include "../section.h" +#include "../task/task.h" +#include "../../list.h" + +void action_browse_boss_ext_save_data(linked_list* items, list_item* selected) { + ext_save_data_info* info = (ext_save_data_info*) selected->data; -void action_browse_boss_ext_save_data(ext_save_data_info* info, bool* populated) { u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)}; FS_Archive archive = {ARCHIVE_BOSS_EXTDATA, {PATH_BINARY, 12, path}}; files_open(archive); diff --git a/source/ui/section/action/browsesystemsavedata.c b/source/ui/section/action/browsesystemsavedata.c index 908618b..84a3e8f 100644 --- a/source/ui/section/action/browsesystemsavedata.c +++ b/source/ui/section/action/browsesystemsavedata.c @@ -2,8 +2,12 @@ #include "action.h" #include "../section.h" +#include "../task/task.h" +#include "../../list.h" + +void action_browse_system_save_data(linked_list* items, list_item* selected) { + system_save_data_info* info = (system_save_data_info*) selected->data; -void action_browse_system_save_data(system_save_data_info* info, bool* populated) { u32 path[2] = {MEDIATYPE_NAND, info->systemSaveDataId}; FS_Archive archive = {ARCHIVE_SYSTEM_SAVEDATA, {PATH_BINARY, 8, path}}; files_open(archive); diff --git a/source/ui/section/action/browsetitlesavedata.c b/source/ui/section/action/browsetitlesavedata.c index 5afad3e..7928575 100644 --- a/source/ui/section/action/browsetitlesavedata.c +++ b/source/ui/section/action/browsetitlesavedata.c @@ -2,8 +2,12 @@ #include "action.h" #include "../section.h" +#include "../task/task.h" +#include "../../list.h" + +void action_browse_title_save_data(linked_list* items, list_item* selected) { + title_info* info = (title_info*) selected->data; -void action_browse_title_save_data(title_info* info, bool* populated) { u32 path[3] = {info->mediaType, (u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF)}; FS_Archive archive = {ARCHIVE_USER_SAVEDATA, {PATH_BINARY, 12, path}}; files_open(archive); diff --git a/source/ui/section/action/browseextsavedata.c b/source/ui/section/action/browseuserextsavedata.c similarity index 61% rename from source/ui/section/action/browseextsavedata.c rename to source/ui/section/action/browseuserextsavedata.c index 3bf8643..cc94b46 100644 --- a/source/ui/section/action/browseextsavedata.c +++ b/source/ui/section/action/browseuserextsavedata.c @@ -2,8 +2,12 @@ #include "action.h" #include "../section.h" +#include "../task/task.h" +#include "../../list.h" + +void action_browse_user_ext_save_data(linked_list* items, list_item* selected) { + ext_save_data_info* info = (ext_save_data_info*) selected->data; -void action_browse_user_ext_save_data(ext_save_data_info* info, bool* populated) { u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)}; FS_Archive archive = {info->shared ? ARCHIVE_SHARED_EXTDATA : ARCHIVE_EXTDATA, {PATH_BINARY, 12, path}}; files_open(archive); diff --git a/source/ui/section/action/clipboard.c b/source/ui/section/action/clipboard.c index c4e5e5e..35fcba1 100644 --- a/source/ui/section/action/clipboard.c +++ b/source/ui/section/action/clipboard.c @@ -1,4 +1,3 @@ -#include #include #include @@ -8,9 +7,10 @@ #include "../task/task.h" static bool clipboard_has = false; +static bool clipboard_contents_only; static FS_Archive clipboard_archive; static void* clipboard_archive_path; -static char clipboard_path[PATH_MAX]; +static char clipboard_path[FILE_PATH_MAX]; bool clipboard_has_contents() { return clipboard_has; @@ -24,12 +24,17 @@ char* clipboard_get_path() { return clipboard_path; } -Result clipboard_set_contents(FS_Archive archive, const char* path) { +bool clipboard_is_contents_only() { + return clipboard_contents_only; +} + +Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly) { clipboard_clear(); clipboard_has = true; + clipboard_contents_only = contentsOnly; clipboard_archive = archive; - strncpy(clipboard_path, path, PATH_MAX); + strncpy(clipboard_path, path, FILE_PATH_MAX); if(clipboard_archive.lowPath.size > 0) { clipboard_archive_path = calloc(1, clipboard_archive.lowPath.size); @@ -58,5 +63,6 @@ void clipboard_clear() { } clipboard_has = false; - clipboard_path[0] = '\0'; + clipboard_contents_only = false; + memset(clipboard_path, '\0', FILE_PATH_MAX); } \ No newline at end of file diff --git a/source/ui/section/action/clipboard.h b/source/ui/section/action/clipboard.h index 535f369..7418e62 100644 --- a/source/ui/section/action/clipboard.h +++ b/source/ui/section/action/clipboard.h @@ -4,8 +4,7 @@ bool clipboard_has_contents(); FS_Archive* clipboard_get_archive(); -char* clipboard_get_name(); char* clipboard_get_path(); -bool clipboard_is_cut(); -Result clipboard_set_contents(FS_Archive archive, const char* path); +bool clipboard_is_contents_only(); +Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly); void clipboard_clear(); \ No newline at end of file diff --git a/source/ui/section/action/copycontents.c b/source/ui/section/action/copycontents.c new file mode 100644 index 0000000..f7dd4a6 --- /dev/null +++ b/source/ui/section/action/copycontents.c @@ -0,0 +1,29 @@ +#include <3ds.h> + +#include "action.h" +#include "clipboard.h" +#include "../task/task.h" +#include "../../error.h" +#include "../../list.h" +#include "../../prompt.h" +#include "../../ui.h" +#include "../../../core/screen.h" + +static void action_copy_contents_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool contentsOnly) { + Result res = 0; + if(R_FAILED(res = clipboard_set_contents(*target->archive, target->path, contentsOnly))) { + error_display_res(NULL, target, ui_draw_file_info, res, "Failed to copy to clipboard."); + + return; + } + + prompt_display("Success", message, COLOR_TEXT, false, target, NULL, ui_draw_file_info, NULL); +} + +void action_copy_content(linked_list* items, list_item* selected, file_info* target) { + action_copy_contents_internal(items, selected, target, "Selected content copied to clipboard.", false); +} + +void action_copy_contents(linked_list* items, list_item* selected, file_info* target) { + action_copy_contents_internal(items, selected, target, "Directory contents copied to clipboard.", true); +} \ No newline at end of file diff --git a/source/ui/section/action/copyfiles.c b/source/ui/section/action/copyfiles.c deleted file mode 100644 index c2d76b7..0000000 --- a/source/ui/section/action/copyfiles.c +++ /dev/null @@ -1,18 +0,0 @@ -#include <3ds.h> - -#include "action.h" -#include "clipboard.h" -#include "../../error.h" -#include "../../prompt.h" -#include "../../../screen.h" - -void action_copy_contents(file_info* info, bool* populated) { - Result res = 0; - if(R_FAILED(res = clipboard_set_contents(*info->archive, info->path))) { - error_display_res(NULL, info, ui_draw_file_info, res, "Failed to copy contents to clipboard."); - - return; - } - - prompt_display("Success", "Content copied to clipboard.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL); -} \ No newline at end of file diff --git a/source/ui/section/action/deletecontents.c b/source/ui/section/action/deletecontents.c index 03a1480..1edbd03 100644 --- a/source/ui/section/action/deletecontents.c +++ b/source/ui/section/action/deletecontents.c @@ -5,16 +5,22 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" -#include "../../../util.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { - file_info* base; - bool* populated; + linked_list* items; + file_info* target; + char** contents; + list_item* curr; data_op_info deleteInfo; Handle cancelEvent; @@ -23,14 +29,20 @@ typedef struct { static Result action_delete_contents_delete(void* data, u32 index) { delete_contents_data* deleteData = (delete_contents_data*) data; + list_item* old = deleteData->curr; + task_create_file_item(&deleteData->curr, deleteData->target->archive, deleteData->contents[index]); + if(old != NULL) { + task_free_file(old); + } + Result res = 0; FS_Path* fsPath = util_make_path_utf8(deleteData->contents[index]); if(fsPath != NULL) { - if(util_is_dir(deleteData->base->archive, deleteData->contents[index])) { - res = FSUSER_DeleteDirectory(*deleteData->base->archive, *fsPath); + if(util_is_dir(deleteData->target->archive, deleteData->contents[index])) { + res = FSUSER_DeleteDirectory(*deleteData->target->archive, *fsPath); } else { - res = FSUSER_DeleteFile(*deleteData->base->archive, *fsPath); + res = FSUSER_DeleteFile(*deleteData->target->archive, *fsPath); } util_free_path_utf8(fsPath); @@ -38,6 +50,21 @@ static Result action_delete_contents_delete(void* data, u32 index) { res = R_FBI_OUT_OF_MEMORY; } + if(R_SUCCEEDED(res)) { + linked_list_iter iter; + linked_list_iterate(deleteData->items, &iter); + + while(linked_list_iter_has_next(&iter)) { + list_item* item = (list_item*) linked_list_iter_next(&iter); + file_info* currInfo = (file_info*) item->data; + + if(strcmp(currInfo->path, deleteData->contents[index]) == 0) { + linked_list_iter_remove(&iter); + break; + } + } + } + return res; } @@ -45,17 +72,11 @@ static bool action_delete_contents_error(void* data, u32 index, Result res) { delete_contents_data* deleteData = (delete_contents_data*) data; if(res == R_FBI_CANCELLED) { - prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, deleteData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, deleteData->target, NULL, ui_draw_file_info, NULL); return false; } else { - char* path = deleteData->contents[index]; - volatile bool dismissed = false; - if(strlen(path) > 48) { - error_display_res(&dismissed, deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.45s...", path); - } else { - error_display_res(&dismissed, deleteData->base, ui_draw_file_info, res, "Failed to delete content.\n%.48s", path); - } + error_display_res(&dismissed, deleteData->curr != NULL ? deleteData->curr->data : deleteData->target, ui_draw_file_info, res, "Failed to delete content."); while(!dismissed) { svcSleepThread(1000000); @@ -66,10 +87,21 @@ static bool action_delete_contents_error(void* data, u32 index, Result res) { } static void action_delete_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_file_info(view, ((delete_contents_data*) data)->base, x1, y1, x2, y2); + delete_contents_data* deleteData = (delete_contents_data*) data; + + if(deleteData->curr != NULL) { + ui_draw_file_info(view, ((delete_contents_data*) data)->curr->data, x1, y1, x2, y2); + } else if(deleteData->target != NULL) { + ui_draw_file_info(view, ((delete_contents_data*) data)->target, x1, y1, x2, y2); + } } static void action_delete_contents_free_data(delete_contents_data* data) { + if(data->curr != NULL) { + task_free_file(data->curr); + data->curr = NULL; + } + util_free_contents(data->contents, data->deleteInfo.total); free(data); } @@ -78,19 +110,15 @@ static void action_delete_contents_update(ui_view* view, void* data, float* prog delete_contents_data* deleteData = (delete_contents_data*) data; if(deleteData->deleteInfo.finished) { - *deleteData->populated = false; - - if(deleteData->base->archive->id == ARCHIVE_USER_SAVEDATA) { - FSUSER_ControlArchive(*deleteData->base->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); + if(deleteData->target->archive->id == ARCHIVE_USER_SAVEDATA) { + FSUSER_ControlArchive(*deleteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); } ui_pop(); info_destroy(view); - if(deleteData->deleteInfo.premature) { - action_delete_contents_free_data(deleteData); - } else { - prompt_display("Success", "Contents deleted.", COLOR_TEXT, false, deleteData->base, NULL, ui_draw_file_info, NULL); + if(!deleteData->deleteInfo.premature) { + prompt_display("Success", "Contents deleted.", COLOR_TEXT, false, deleteData->target, NULL, ui_draw_file_info, NULL); } action_delete_contents_free_data(deleteData); @@ -121,7 +149,7 @@ static void action_delete_contents_onresponse(ui_view* view, void* data, bool re } } -static void action_delete_contents_internal(file_info* info, bool* populated, const char* message, bool recursive, void* filterData, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) { +static void action_delete_contents_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool recursive, void* filterData, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) { delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete contents data."); @@ -129,8 +157,8 @@ static void action_delete_contents_internal(file_info* info, bool* populated, co return; } - data->base = info; - data->populated = populated; + data->items = items; + data->target = target; data->deleteInfo.data = data; @@ -143,8 +171,8 @@ static void action_delete_contents_internal(file_info* info, bool* populated, co data->cancelEvent = 0; Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->deleteInfo.total, info->archive, info->path, recursive, false, filterData, filter))) { - error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(R_FAILED(res = util_populate_contents(&data->contents, &data->deleteInfo.total, target->archive, target->path, recursive, false, filterData, filter))) { + error_display_res(NULL, target, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; @@ -153,18 +181,22 @@ static void action_delete_contents_internal(file_info* info, bool* populated, co prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse); } -void action_delete_contents(file_info* info, bool* populated) { - action_delete_contents_internal(info, populated, "Delete the selected content?", true, NULL, NULL); +void action_delete_contents(linked_list* items, list_item* selected, file_info* target) { + action_delete_contents_internal(items, selected, target, "Delete the selected content?", true, NULL, NULL); } -void action_delete_dir(file_info* info, bool* populated) { - action_delete_contents_internal(info, populated, "Delete the current directory?", true, NULL, NULL); +void action_delete_dir(linked_list* items, list_item* selected, file_info* target) { + action_delete_contents_internal(items, selected, target, "Delete the current directory?", true, NULL, NULL); } -void action_delete_dir_contents(file_info* info, bool* populated) { - action_delete_contents_internal(info, populated, "Delete all contents of the current directory?", true, info->path, util_filter_not_path); +void action_delete_dir_contents(linked_list* items, list_item* selected, file_info* target) { + action_delete_contents_internal(items, selected, target, "Delete all contents of the current directory?", true, target->path, util_filter_not_path); } -void action_delete_dir_cias(file_info* info, bool* populated) { - action_delete_contents_internal(info, populated, "Delete all CIAs in the current directory?", false, ".cia", util_filter_file_extension); +void action_delete_dir_cias(linked_list* items, list_item* selected, file_info* target) { + action_delete_contents_internal(items, selected, target, "Delete all CIAs in the current directory?", false, ".cia", util_filter_file_extension); +} + +void action_delete_dir_tickets(linked_list* items, list_item* selected, file_info* target) { + action_delete_contents_internal(items, selected, target, "Delete all tickets in the current directory?", false, ".tik", util_filter_file_extension); } \ No newline at end of file diff --git a/source/ui/section/action/deleteextsavedata.c b/source/ui/section/action/deleteextsavedata.c index a6c2de1..0397f24 100644 --- a/source/ui/section/action/deleteextsavedata.c +++ b/source/ui/section/action/deleteextsavedata.c @@ -3,35 +3,41 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" typedef struct { - ext_save_data_info* info; - bool* populated; + linked_list* items; + list_item* selected; } delete_ext_save_data_data; static void action_delete_ext_save_data_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_ext_save_data_info(view, ((delete_ext_save_data_data*) data)->info, x1, y1, x2, y2); + ui_draw_ext_save_data_info(view, ((delete_ext_save_data_data*) data)->selected->data, x1, y1, x2, y2); } static void action_delete_ext_save_data_update(ui_view* view, void* data, float* progress, char* text) { delete_ext_save_data_data* deleteData = (delete_ext_save_data_data*) data; + ext_save_data_info* info = (ext_save_data_info*) deleteData->selected->data; - FS_ExtSaveDataInfo extInfo = {.mediaType = deleteData->info->mediaType, .saveId = deleteData->info->extSaveDataId}; + FS_ExtSaveDataInfo extInfo = {.mediaType = info->mediaType, .saveId = info->extSaveDataId}; Result res = FSUSER_DeleteExtSaveData(extInfo); ui_pop(); info_destroy(view); if(R_FAILED(res)) { - error_display_res(NULL, deleteData->info, ui_draw_ext_save_data_info, res, "Failed to delete ext save data."); + error_display_res(NULL, info, ui_draw_ext_save_data_info, res, "Failed to delete ext save data."); } else { - *deleteData->populated = false; + linked_list_remove(deleteData->items, deleteData->selected); + task_free_ext_save_data(deleteData->selected); - prompt_display("Success", "Ext save data deleted.", COLOR_TEXT, false, deleteData->info, NULL, ui_draw_ext_save_data_info, NULL); + prompt_display("Success", "Ext save data deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } free(data); @@ -45,7 +51,7 @@ static void action_delete_ext_save_data_onresponse(ui_view* view, void* data, bo } } -void action_delete_ext_save_data(ext_save_data_info* info, bool* populated) { +void action_delete_ext_save_data(linked_list* items, list_item* selected) { delete_ext_save_data_data* data = (delete_ext_save_data_data*) calloc(1, sizeof(delete_ext_save_data_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete ext save data data."); @@ -53,8 +59,8 @@ void action_delete_ext_save_data(ext_save_data_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; prompt_display("Confirmation", "Delete the selected ext save data?", COLOR_TEXT, true, data, NULL, action_delete_ext_save_data_draw_top, action_delete_ext_save_data_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/deletependingtitles.c b/source/ui/section/action/deletependingtitles.c index a4d3884..dc5989f 100644 --- a/source/ui/section/action/deletependingtitles.c +++ b/source/ui/section/action/deletependingtitles.c @@ -4,18 +4,20 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" typedef struct { - pending_title_info* info; - bool* populated; - u64* titleIds; + linked_list* items; + list_item* selected; - u32 sdTotal; - u32 nandTotal; + bool all; data_op_info deleteInfo; Handle cancelEvent; @@ -24,10 +26,14 @@ typedef struct { static Result action_delete_pending_titles_delete(void* data, u32 index) { delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; + list_item* item = deleteData->all ? (list_item*) linked_list_get(deleteData->items, index) : deleteData->selected; + pending_title_info* info = (pending_title_info*) item->data; + Result res = 0; - if(R_SUCCEEDED(res = AM_DeletePendingTitle(index >= deleteData->sdTotal ? MEDIATYPE_NAND : MEDIATYPE_SD, deleteData->titleIds[index]))) { - *deleteData->populated = false; + if(R_SUCCEEDED(res = AM_DeletePendingTitle(info->mediaType, info->titleId))) { + linked_list_remove(deleteData->items, item); + task_free_pending_title(item); } return res; @@ -36,14 +42,14 @@ static Result action_delete_pending_titles_delete(void* data, u32 index) { static bool action_delete_pending_titles_error(void* data, u32 index, Result res) { delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; + pending_title_info* info = (pending_title_info*) (deleteData->all ? ((list_item*) linked_list_get(deleteData->items, index))->data : deleteData->selected->data); + if(res == R_FBI_CANCELLED) { - prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, deleteData->info, NULL, deleteData->info != NULL ? ui_draw_pending_title_info : NULL, NULL); + prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_pending_title_info, NULL); return false; } else { - u64 titleId = deleteData->titleIds[index]; - volatile bool dismissed = false; - error_display_res(&dismissed, deleteData->info, deleteData->info != NULL ? ui_draw_pending_title_info : NULL, res, "Failed to delete pending title.\n%llX", titleId); + error_display_res(&dismissed, info, ui_draw_pending_title_info, res, "Failed to delete pending title."); while(!dismissed) { svcSleepThread(1000000); @@ -56,16 +62,12 @@ static bool action_delete_pending_titles_error(void* data, u32 index, Result res static void action_delete_pending_titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; - if(deleteData->info != NULL) { - ui_draw_pending_title_info(view, deleteData->info, x1, y1, x2, y2); + u32 index = deleteData->deleteInfo.processed; + if(index < deleteData->deleteInfo.total) { + ui_draw_pending_title_info(view, (pending_title_info*) (deleteData->all ? ((list_item*) linked_list_get(deleteData->items, index))->data : deleteData->selected->data), x1, y1, x2, y2); } } -static void action_delete_pending_titles_free_data(delete_pending_titles_data* data) { - free(data->titleIds); - free(data); -} - static void action_delete_pending_titles_update(ui_view* view, void* data, float* progress, char* text) { delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data; @@ -73,13 +75,11 @@ static void action_delete_pending_titles_update(ui_view* view, void* data, float ui_pop(); info_destroy(view); - if(deleteData->deleteInfo.premature) { - action_delete_pending_titles_free_data(deleteData); - } else { - prompt_display("Success", "Pending title(s) deleted.", COLOR_TEXT, false, deleteData->info, NULL, deleteData->info != NULL ? ui_draw_pending_title_info : NULL, NULL); + if(!deleteData->deleteInfo.premature) { + prompt_display("Success", "Pending title(s) deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } - action_delete_pending_titles_free_data(deleteData); + free(deleteData); return; } @@ -103,30 +103,28 @@ static void action_delete_pending_titles_onresponse(ui_view* view, void* data, b error_display(NULL, NULL, NULL, "Failed to initiate delete operation."); } } else { - action_delete_pending_titles_free_data(deleteData); + free(deleteData); } } -void action_delete_pending_titles(pending_title_info* info, bool* populated, const char* message, u64* titleIds, u32 sdTotal, u32 nandTotal) { +void action_delete_pending_titles(linked_list* items, list_item* selected, const char* message, bool all) { delete_pending_titles_data* data = (delete_pending_titles_data*) calloc(1, sizeof(delete_pending_titles_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete pending titles data."); - free(titleIds); return; } - data->info = info; - data->populated = populated; - data->titleIds = titleIds; - data->sdTotal = sdTotal; - data->nandTotal = nandTotal; + data->items = items; + data->selected = selected; + + data->all = all; data->deleteInfo.data = data; data->deleteInfo.op = DATAOP_DELETE; - data->deleteInfo.total = sdTotal + nandTotal; + data->deleteInfo.total = all ? linked_list_size(items) : 1; data->deleteInfo.delete = action_delete_pending_titles_delete; @@ -134,48 +132,13 @@ void action_delete_pending_titles(pending_title_info* info, bool* populated, con data->cancelEvent = 0; - prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_delete_pending_titles_draw_top, action_delete_pending_titles_onresponse); + prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, !all ? action_delete_pending_titles_draw_top : NULL, action_delete_pending_titles_onresponse); } -void action_delete_pending_title(pending_title_info* info, bool* populated) { - u64* titleIds = (u64*) calloc(1, sizeof(u64)); - if(titleIds == NULL) { - error_display(NULL, NULL, NULL, "Failed to allocate title ID buffer."); - - return; - } - - titleIds[0] = info->titleId; - u32 sdTotal = info->mediaType == MEDIATYPE_SD ? 1 : 0; - u32 nandTotal = info->mediaType == MEDIATYPE_NAND ? 1 : 0; - - action_delete_pending_titles(info, populated, "Delete the selected pending title?", titleIds, sdTotal, nandTotal); +void action_delete_pending_title(linked_list* items, list_item* selected) { + action_delete_pending_titles(items, selected, "Delete the selected pending title?", false); } -void action_delete_all_pending_titles(pending_title_info* info, bool* populated) { - u32 sdTotal = 0; - u32 nandTotal = 0; - - Result res = 0; - if(R_FAILED(res = AM_GetPendingTitleCount(&sdTotal, MEDIATYPE_SD, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION)) || R_FAILED(res = AM_GetPendingTitleCount(&nandTotal, MEDIATYPE_NAND, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION))) { - error_display_res(NULL, NULL, NULL, res, "Failed to retrieve pending title count."); - - return; - } - - u64* titleIds = (u64*) calloc(sdTotal + nandTotal, sizeof(u64)); - if(titleIds == NULL) { - error_display(NULL, NULL, NULL, "Failed to allocate title ID buffer."); - - return; - } - - if(R_FAILED(res = AM_GetPendingTitleList(&sdTotal, sdTotal, MEDIATYPE_SD, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, titleIds)) || R_FAILED(res = AM_GetPendingTitleList(&nandTotal, nandTotal, MEDIATYPE_NAND, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, &titleIds[sdTotal]))) { - error_display_res(NULL, NULL, NULL, res, "Failed to retrieve pending title list."); - - free(titleIds); - return; - } - - action_delete_pending_titles(info, populated, "Delete all pending titles?", titleIds, sdTotal, nandTotal); +void action_delete_all_pending_titles(linked_list* items, list_item* selected) { + action_delete_pending_titles(items, selected, "Delete all pending titles?", true); } \ No newline at end of file diff --git a/source/ui/section/action/deletesecurevalue.c b/source/ui/section/action/deletesecurevalue.c index 39a5e5b..2843e8f 100644 --- a/source/ui/section/action/deletesecurevalue.c +++ b/source/ui/section/action/deletesecurevalue.c @@ -3,10 +3,14 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" static void action_delete_secure_value_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; @@ -31,6 +35,6 @@ static void action_delete_secure_value_onresponse(ui_view* view, void* data, boo } } -void action_delete_secure_value(title_info* info, bool* populated) { - prompt_display("Confirmation", "Delete the secure value of the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_delete_secure_value_onresponse); +void action_delete_secure_value(linked_list* items, list_item* selected) { + prompt_display("Confirmation", "Delete the secure value of the selected title?", COLOR_TEXT, true, selected->data, NULL, ui_draw_title_info, action_delete_secure_value_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/deletesystemsavedata.c b/source/ui/section/action/deletesystemsavedata.c index b891228..8309ff3 100644 --- a/source/ui/section/action/deletesystemsavedata.c +++ b/source/ui/section/action/deletesystemsavedata.c @@ -3,35 +3,42 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" typedef struct { - system_save_data_info* info; - bool* populated; + linked_list* items; + list_item* selected; } delete_system_save_data_data; static void action_delete_system_save_data_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_system_save_data_info(view, ((delete_system_save_data_data*) data)->info, x1, y1, x2, y2); + ui_draw_system_save_data_info(view, ((delete_system_save_data_data*) data)->selected->data, x1, y1, x2, y2); } static void action_delete_system_save_data_update(ui_view* view, void* data, float* progress, char* text) { delete_system_save_data_data* deleteData = (delete_system_save_data_data*) data; - FS_SystemSaveDataInfo sysInfo = {.mediaType = MEDIATYPE_NAND, .saveId = deleteData->info->systemSaveDataId}; + system_save_data_info* info = (system_save_data_info*) deleteData->selected->data; + + FS_SystemSaveDataInfo sysInfo = {.mediaType = MEDIATYPE_NAND, .saveId = info->systemSaveDataId}; Result res = FSUSER_DeleteSystemSaveData(sysInfo); ui_pop(); info_destroy(view); if(R_FAILED(res)) { - error_display_res(NULL, deleteData->info, ui_draw_system_save_data_info, res, "Failed to delete system save data."); + error_display_res(NULL, info, ui_draw_system_save_data_info, res, "Failed to delete system save data."); } else { - *deleteData->populated = false; + linked_list_remove(deleteData->items, deleteData->selected); + task_free_system_save_data(deleteData->selected); - prompt_display("Success", "System save data deleted.", COLOR_TEXT, false, deleteData->info, NULL, ui_draw_system_save_data_info, NULL); + prompt_display("Success", "System save data deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } free(data); @@ -45,7 +52,7 @@ static void action_delete_system_save_data_onresponse(ui_view* view, void* data, } } -void action_delete_system_save_data(system_save_data_info* info, bool* populated) { +void action_delete_system_save_data(linked_list* items, list_item* selected) { delete_system_save_data_data* data = (delete_system_save_data_data*) calloc(1, sizeof(delete_system_save_data_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete system save data data."); @@ -53,8 +60,8 @@ void action_delete_system_save_data(system_save_data_info* info, bool* populated return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; prompt_display("Confirmation", "Delete the selected system save data?", COLOR_TEXT, true, data, NULL, action_delete_system_save_data_draw_top, action_delete_system_save_data_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/deleteticket.c b/source/ui/section/action/deleteticket.c index c8e0d5a..d235694 100644 --- a/source/ui/section/action/deleteticket.c +++ b/source/ui/section/action/deleteticket.c @@ -3,34 +3,41 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" typedef struct { - ticket_info* info; - bool* populated; + linked_list* items; + list_item* selected; } delete_ticket_data; static void action_delete_ticket_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_ticket_info(view, ((delete_ticket_data*) data)->info, x1, y1, x2, y2); + ui_draw_ticket_info(view, ((delete_ticket_data*) data)->selected->data, x1, y1, x2, y2); } static void action_delete_ticket_update(ui_view* view, void* data, float* progress, char* text) { delete_ticket_data* deleteData = (delete_ticket_data*) data; - Result res = AM_DeleteTicket(deleteData->info->titleId); + ticket_info* info = (ticket_info*) deleteData->selected->data; + + Result res = AM_DeleteTicket(info->titleId); ui_pop(); info_destroy(view); if(R_FAILED(res)) { - error_display_res(NULL, deleteData->info, ui_draw_ticket_info, res, "Failed to delete ticket."); + error_display_res(NULL, info, ui_draw_ticket_info, res, "Failed to delete ticket."); } else { - *deleteData->populated = false; + linked_list_remove(deleteData->items, deleteData->selected); + task_free_ticket(deleteData->selected); - prompt_display("Success", "Ticket deleted.", COLOR_TEXT, false, deleteData->info, NULL, ui_draw_ticket_info, NULL); + prompt_display("Success", "Ticket deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } free(data); @@ -44,7 +51,7 @@ static void action_delete_ticket_onresponse(ui_view* view, void* data, bool resp } } -void action_delete_ticket(ticket_info* info, bool* populated) { +void action_delete_ticket(linked_list* items, list_item* selected) { delete_ticket_data* data = (delete_ticket_data*) calloc(1, sizeof(delete_ticket_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete ticket data."); @@ -52,8 +59,8 @@ void action_delete_ticket(ticket_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; prompt_display("Confirmation", "Delete the selected ticket?", COLOR_TEXT, true, data, NULL, action_delete_ticket_draw_top, action_delete_ticket_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/deletetitle.c b/source/ui/section/action/deletetitle.c index c246573..6f6bdf7 100644 --- a/source/ui/section/action/deletetitle.c +++ b/source/ui/section/action/deletetitle.c @@ -3,34 +3,41 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" typedef struct { - title_info* info; - bool* populated; + linked_list* items; + list_item* selected; } delete_title_data; static void action_delete_title_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_title_info(view, ((delete_title_data*) data)->info, x1, y1, x2, y2); + ui_draw_title_info(view, ((delete_title_data*) data)->selected->data, x1, y1, x2, y2); } static void action_delete_title_update(ui_view* view, void* data, float* progress, char* text) { delete_title_data* deleteData = (delete_title_data*) data; - Result res = AM_DeleteTitle(deleteData->info->mediaType, deleteData->info->titleId); + title_info* info = (title_info*) deleteData->selected->data; + + Result res = AM_DeleteTitle(info->mediaType, info->titleId); ui_pop(); info_destroy(view); if(R_FAILED(res)) { - error_display_res(NULL, deleteData->info, ui_draw_title_info, res, "Failed to delete title."); + error_display_res(NULL, info, ui_draw_title_info, res, "Failed to delete title."); } else { - *deleteData->populated = false; + linked_list_remove(deleteData->items, deleteData->selected); + task_free_title(deleteData->selected); - prompt_display("Success", "Title deleted.", COLOR_TEXT, false, deleteData->info, NULL, ui_draw_title_info, NULL); + prompt_display("Success", "Title deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } free(data); @@ -44,7 +51,7 @@ static void action_delete_title_onresponse(ui_view* view, void* data, bool respo } } -void action_delete_title(title_info* info, bool* populated) { +void action_delete_title(linked_list* items, list_item* selected) { delete_title_data* data = (delete_title_data*) calloc(1, sizeof(delete_title_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate delete title data."); @@ -52,8 +59,8 @@ void action_delete_title(title_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; prompt_display("Confirmation", "Delete the selected title?", COLOR_TEXT, true, data, NULL, action_delete_title_draw_top, action_delete_title_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/exportsecurevalue.c b/source/ui/section/action/exportsecurevalue.c index b9cbcf2..a75a428 100644 --- a/source/ui/section/action/exportsecurevalue.c +++ b/source/ui/section/action/exportsecurevalue.c @@ -3,11 +3,15 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../util.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" static void action_export_secure_value_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; @@ -67,6 +71,6 @@ static void action_export_secure_value_onresponse(ui_view* view, void* data, boo } } -void action_export_secure_value(title_info* info, bool* populated) { - prompt_display("Confirmation", "Export the secure value of the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_export_secure_value_onresponse); +void action_export_secure_value(linked_list* items, list_item* selected) { + prompt_display("Confirmation", "Export the secure value of the selected title?", COLOR_TEXT, true, selected->data, NULL, ui_draw_title_info, action_export_secure_value_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/extractsmdh.c b/source/ui/section/action/extractsmdh.c index 07d9e62..385c7b3 100644 --- a/source/ui/section/action/extractsmdh.c +++ b/source/ui/section/action/extractsmdh.c @@ -4,11 +4,15 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../util.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" static void action_extract_smdh_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; @@ -22,38 +26,33 @@ static void action_extract_smdh_update(ui_view* view, void* data, float* progres Handle fileHandle; if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) { - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - u32 bytesRead; - if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { - FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}}; - if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) { - if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/smdh/"))) { - char pathBuf[64]; - snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId); + SMDH smdh; - FS_Path* fsPath = util_make_path_utf8(pathBuf); - if(fsPath != NULL) { - Handle smdhHandle = 0; - if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { - u32 bytesWritten = 0; - res = FSFILE_Write(smdhHandle, &bytesWritten, 0, smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); - FSFILE_Close(smdhHandle); - } + u32 bytesRead = 0; + if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) { + FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}}; + if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) { + if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/smdh/"))) { + char pathBuf[64]; + snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId); - util_free_path_utf8(fsPath); - } else { - res = R_FBI_OUT_OF_MEMORY; + FS_Path* fsPath = util_make_path_utf8(pathBuf); + if(fsPath != NULL) { + Handle smdhHandle = 0; + if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) { + u32 bytesWritten = 0; + res = FSFILE_Write(smdhHandle, &bytesWritten, 0, &smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME); + FSFILE_Close(smdhHandle); } + + util_free_path_utf8(fsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; } - - FSUSER_CloseArchive(&sdmcArchive); } - } - free(smdh); - } else { - res = R_FBI_OUT_OF_MEMORY; + FSUSER_CloseArchive(&sdmcArchive); + } } FSFILE_Close(fileHandle); @@ -75,6 +74,6 @@ static void action_extract_smdh_onresponse(ui_view* view, void* data, bool respo } } -void action_extract_smdh(title_info* info, bool* populated) { - prompt_display("Confirmation", "Extract the SMDH of the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_extract_smdh_onresponse); +void action_extract_smdh(linked_list* items, list_item* selected) { + prompt_display("Confirmation", "Extract the SMDH of the selected title?", COLOR_TEXT, true, selected->data, NULL, ui_draw_title_info, action_extract_smdh_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/importsecurevalue.c b/source/ui/section/action/importsecurevalue.c index 53dda63..528b924 100644 --- a/source/ui/section/action/importsecurevalue.c +++ b/source/ui/section/action/importsecurevalue.c @@ -3,11 +3,15 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" -#include "../../../util.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" static void action_import_secure_value_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; @@ -52,6 +56,6 @@ static void action_import_secure_value_onresponse(ui_view* view, void* data, boo } } -void action_import_secure_value(title_info* info, bool* populated) { - prompt_display("Confirmation", "Import the secure value of the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_import_secure_value_onresponse); +void action_import_secure_value(linked_list* items, list_item* selected) { + prompt_display("Confirmation", "Import the secure value of the selected title?", COLOR_TEXT, true, selected->data, NULL, ui_draw_title_info, action_import_secure_value_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/installcdn.c b/source/ui/section/action/installcdn.c index 43fe181..a614631 100644 --- a/source/ui/section/action/installcdn.c +++ b/source/ui/section/action/installcdn.c @@ -4,10 +4,14 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" #define CONTENTS_MAX 64 @@ -242,7 +246,7 @@ static void action_install_cdn_onresponse(ui_view* view, void* data, bool respon } } -void action_install_cdn(ticket_info* info, bool* populated) { +void action_install_cdn(linked_list* items, list_item* selected) { install_cdn_data* data = (install_cdn_data*) calloc(1, sizeof(install_cdn_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate install CDN data."); @@ -250,7 +254,7 @@ void action_install_cdn(ticket_info* info, bool* populated) { return; } - data->ticket = info; + data->ticket = (ticket_info*) selected->data; data->responseCode = 0; diff --git a/source/ui/section/action/installcias.c b/source/ui/section/action/installcias.c index 06b1ed7..b3fd95a 100644 --- a/source/ui/section/action/installcias.c +++ b/source/ui/section/action/installcias.c @@ -5,18 +5,27 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" -#include "../../../util.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { - file_info* base; - bool delete; - bool* populated; - char** contents; + linked_list* items; + list_item* selected; + file_info* target; + list_item* curr; + + bool all; + bool delete; + + u32 numDeleted; u64 currTitleId; data_op_info installInfo; @@ -35,11 +44,32 @@ static Result action_install_cias_make_dst_directory(void* data, u32 index) { static Result action_install_cias_open_src(void* data, u32 index, u32* handle) { install_cias_data* installData = (install_cias_data*) data; + if(installData->all) { + linked_list_iter iter; + linked_list_iterate(installData->items, &iter); + + u32 count = 0; + while(linked_list_iter_has_next(&iter) && count < index + 1 - installData->numDeleted) { + list_item* item = linked_list_iter_next(&iter); + file_info* info = (file_info*) item->data; + + size_t len = strlen(info->path); + if(len > 4 && strcmp(&info->path[len - 4], ".cia") == 0) { + installData->curr = item; + count++; + } + } + } else { + installData->curr = installData->selected; + } + + file_info* info = (file_info*) installData->curr->data; + Result res = 0; - FS_Path* fsPath = util_make_path_utf8(installData->contents[index]); + FS_Path* fsPath = util_make_path_utf8(info->path); if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, *installData->base->archive, *fsPath, FS_OPEN_READ, 0); + res = FSUSER_OpenFile(handle, *info->archive, *fsPath, FS_OPEN_READ, 0); util_free_path_utf8(fsPath); } else { @@ -52,11 +82,21 @@ static Result action_install_cias_open_src(void* data, u32 index, u32* handle) { static Result action_install_cias_close_src(void* data, u32 index, bool succeeded, u32 handle) { install_cias_data* installData = (install_cias_data*) data; + file_info* info = (file_info*) installData->curr->data; + Result res = 0; + if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) { - FS_Path* fsPath = util_make_path_utf8(installData->contents[index]); + FS_Path* fsPath = util_make_path_utf8(info->path); if(fsPath != NULL) { - FSUSER_DeleteFile(*installData->base->archive, *fsPath); + if(R_SUCCEEDED(FSUSER_DeleteFile(*info->archive, *fsPath))) { + linked_list_remove(installData->items, installData->curr); + task_free_file(installData->curr); + + installData->curr = NULL; + + installData->numDeleted++; + } util_free_path_utf8(fsPath); } else { @@ -133,25 +173,17 @@ static Result action_install_cias_write_dst(void* data, u32 handle, u32* bytesWr bool action_install_cias_error(void* data, u32 index, Result res) { install_cias_data* installData = (install_cias_data*) data; + file_info* info = (file_info*) installData->curr->data; + if(res == R_FBI_CANCELLED) { - prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL); return false; } else { - char* path = installData->contents[index]; - volatile bool dismissed = false; if(res == R_FBI_WRONG_SYSTEM) { - if(strlen(path) > 48) { - error_display(&dismissed, installData->base, ui_draw_file_info, "Failed to install CIA file.\n%.45s...\nAttempted to install N3DS title to O3DS.", path); - } else { - error_display(&dismissed, installData->base, ui_draw_file_info, "Failed to install CIA file.\n%.48s\nAttempted to install N3DS title to O3DS.", path); - } + error_display(&dismissed, info, ui_draw_file_info, "Failed to install CIA file.\nAttempted to install N3DS title to O3DS."); } else { - if(strlen(path) > 48) { - error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.45s...", path); - } else { - error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install CIA file.\n%.48s", path); - } + error_display_res(&dismissed, info, ui_draw_file_info, res, "Failed to install CIA file."); } while(!dismissed) { @@ -163,30 +195,27 @@ bool action_install_cias_error(void* data, u32 index, Result res) { } static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_file_info(view, ((install_cias_data*) data)->base, x1, y1, x2, y2); -} + install_cias_data* installData = (install_cias_data*) data; -static void action_install_cias_free_data(install_cias_data* data) { - util_free_contents(data->contents, data->installInfo.total); - free(data); + if(installData->curr != NULL) { + ui_draw_file_info(view, installData->curr->data, x1, y1, x2, y2); + } else if(installData->target != NULL) { + ui_draw_file_info(view, installData->target, x1, y1, x2, y2); + } } static void action_install_cias_update(ui_view* view, void* data, float* progress, char* text) { install_cias_data* installData = (install_cias_data*) data; if(installData->installInfo.finished) { - if(installData->delete) { - *installData->populated = false; - } - ui_pop(); info_destroy(view); if(!installData->installInfo.premature) { - prompt_display("Success", "Install finished.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Success", "Install finished.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } - action_install_cias_free_data(installData); + free(installData); return; } @@ -207,16 +236,16 @@ static void action_install_cias_onresponse(ui_view* view, void* data, bool respo if(installData->cancelEvent != 0) { info_display("Installing CIA(s)", "Press B to cancel.", true, data, action_install_cias_update, action_install_cias_draw_top); } else { - error_display(NULL, installData->base, ui_draw_file_info, "Failed to initiate CIA installation."); + error_display(NULL, NULL, NULL, "Failed to initiate CIA installation."); - action_install_cias_free_data(installData); + free(installData); } } else { - action_install_cias_free_data(installData); + free(installData); } } -static void action_install_cias_internal(file_info* info, bool* populated, bool delete) { +static void action_install_cias_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool all, bool delete) { install_cias_data* data = (install_cias_data*) calloc(1, sizeof(install_cias_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate install CIAs data."); @@ -224,10 +253,14 @@ static void action_install_cias_internal(file_info* info, bool* populated, bool return; } - data->base = info; - data->delete = delete; - data->populated = populated; + data->items = items; + data->selected = selected; + data->target = target; + data->all = all; + data->delete = delete; + + data->numDeleted = 0; data->currTitleId = 0; data->installInfo.data = data; @@ -252,21 +285,38 @@ static void action_install_cias_internal(file_info* info, bool* populated, bool data->cancelEvent = 0; - Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->installInfo.total, info->archive, info->path, false, false, ".cia", util_filter_file_extension))) { - error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(all) { + linked_list_iter iter; + linked_list_iterate(data->items, &iter); - free(data); - return; + while(linked_list_iter_has_next(&iter)) { + list_item* item = linked_list_iter_next(&iter); + file_info* info = (file_info*) item->data; + + size_t len = strlen(info->path); + if(len > 4 && strcmp(&info->path[len - 4], ".cia") == 0) { + data->installInfo.total++; + } + } + } else { + data->installInfo.total = 1; } - prompt_display("Confirmation", "Install the selected CIA(s)?", COLOR_TEXT, true, data, NULL, action_install_cias_draw_top, action_install_cias_onresponse); + prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_install_cias_draw_top, action_install_cias_onresponse); } -void action_install_cias(file_info* info, bool* populated) { - action_install_cias_internal(info, populated, false); +void action_install_cia(linked_list* items, list_item* selected, file_info* target) { + action_install_cias_internal(items, selected, target, "Install the selected CIA?", false, false); } -void action_install_cias_delete(file_info* info, bool* populated) { - action_install_cias_internal(info, populated, true); +void action_install_cia_delete(linked_list* items, list_item* selected, file_info* target) { + action_install_cias_internal(items, selected, target, "Install and delete the selected CIA?", false, true); +} + +void action_install_cias(linked_list* items, list_item* selected, file_info* target) { + action_install_cias_internal(items, selected, target, "Install all CIAs in the current directory?", true, false); +} + +void action_install_cias_delete(linked_list* items, list_item* selected, file_info* target) { + action_install_cias_internal(items, selected, target, "Install and delete all CIAs in the current directory?", true, true); } \ No newline at end of file diff --git a/source/ui/section/action/installtickets.c b/source/ui/section/action/installtickets.c index 34ea8fe..9818517 100644 --- a/source/ui/section/action/installtickets.c +++ b/source/ui/section/action/installtickets.c @@ -5,15 +5,27 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" -#include "../../../util.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { - file_info* base; - char** contents; + linked_list* items; + list_item* selected; + file_info* target; + + list_item* curr; + + bool all; + bool delete; + + u32 numDeleted; data_op_info installInfo; Handle cancelEvent; @@ -31,11 +43,32 @@ static Result action_install_tickets_make_dst_directory(void* data, u32 index) { static Result action_install_tickets_open_src(void* data, u32 index, u32* handle) { install_tickets_data* installData = (install_tickets_data*) data; + if(installData->all) { + linked_list_iter iter; + linked_list_iterate(installData->items, &iter); + + u32 count = 0; + while(linked_list_iter_has_next(&iter) && count < index + 1 - installData->numDeleted) { + list_item* item = linked_list_iter_next(&iter); + file_info* info = (file_info*) item->data; + + size_t len = strlen(info->path); + if(len > 4 && strcmp(&info->path[len - 4], ".tik") == 0) { + installData->curr = item; + count++; + } + } + } else { + installData->curr = installData->selected; + } + + file_info* info = (file_info*) installData->curr->data; + Result res = 0; - FS_Path* fsPath = util_make_path_utf8(installData->contents[index]); + FS_Path* fsPath = util_make_path_utf8(info->path); if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, *installData->base->archive, *fsPath, FS_OPEN_READ, 0); + res = FSUSER_OpenFile(handle, *info->archive, *fsPath, FS_OPEN_READ, 0); util_free_path_utf8(fsPath); } else { @@ -46,7 +79,31 @@ static Result action_install_tickets_open_src(void* data, u32 index, u32* handle } static Result action_install_tickets_close_src(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); + install_tickets_data* installData = (install_tickets_data*) data; + + file_info* info = (file_info*) installData->curr->data; + + Result res = 0; + + if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) { + FS_Path* fsPath = util_make_path_utf8(info->path); + if(fsPath != NULL) { + if(R_SUCCEEDED(FSUSER_DeleteFile(*info->archive, *fsPath))) { + linked_list_remove(installData->items, installData->curr); + task_free_file(installData->curr); + + installData->curr = NULL; + + installData->numDeleted++; + } + + util_free_path_utf8(fsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + } + + return res; } static Result action_install_tickets_get_src_size(void* data, u32 handle, u64* size) { @@ -76,18 +133,14 @@ static Result action_install_tickets_write_dst(void* data, u32 handle, u32* byte static bool action_install_tickets_error(void* data, u32 index, Result res) { install_tickets_data* installData = (install_tickets_data*) data; + file_info* info = (file_info*) installData->curr->data; + if(res == R_FBI_CANCELLED) { - prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL); return false; } else { - char* path = installData->contents[index]; - volatile bool dismissed = false; - if(strlen(path) > 48) { - error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.45s...", path); - } else { - error_display_res(&dismissed, installData->base, ui_draw_file_info, res, "Failed to install ticket.\n%.48s", path); - } + error_display_res(&dismissed, info, ui_draw_file_info, res, "Failed to install ticket."); while(!dismissed) { svcSleepThread(1000000); @@ -98,12 +151,13 @@ static bool action_install_tickets_error(void* data, u32 index, Result res) { } static void action_install_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_file_info(view, ((install_tickets_data*) data)->base, x1, y1, x2, y2); -} + install_tickets_data* installData = (install_tickets_data*) data; -static void action_install_tickets_free_data(install_tickets_data* data) { - util_free_contents(data->contents, data->installInfo.total); - free(data); + if(installData->curr != NULL) { + ui_draw_file_info(view, installData->curr->data, x1, y1, x2, y2); + } else if(installData->target != NULL) { + ui_draw_file_info(view, installData->target, x1, y1, x2, y2); + } } static void action_install_tickets_update(ui_view* view, void* data, float* progress, char* text) { @@ -114,10 +168,10 @@ static void action_install_tickets_update(ui_view* view, void* data, float* prog info_destroy(view); if(!installData->installInfo.premature) { - prompt_display("Success", "Install finished.", COLOR_TEXT, false, installData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Success", "Install finished.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); } - action_install_tickets_free_data(installData); + free(installData); return; } @@ -138,16 +192,16 @@ static void action_install_tickets_onresponse(ui_view* view, void* data, bool re if(installData->cancelEvent != 0) { info_display("Installing ticket(s)", "Press B to cancel.", true, data, action_install_tickets_update, action_install_tickets_draw_top); } else { - error_display(NULL, installData->base, ui_draw_file_info, "Failed to initiate ticket installation."); + error_display(NULL, NULL, NULL, "Failed to initiate ticket installation."); - action_install_tickets_free_data(installData); + free(installData); } } else { - action_install_tickets_free_data(installData); + free(installData); } } -void action_install_tickets(file_info* info, bool* populated) { +static void action_install_tickets_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool all, bool delete) { install_tickets_data* data = (install_tickets_data*) calloc(1, sizeof(install_tickets_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate install tickets data."); @@ -155,7 +209,14 @@ void action_install_tickets(file_info* info, bool* populated) { return; } - data->base = info; + data->items = items; + data->selected = selected; + data->target = target; + + data->all = all; + data->delete = delete; + + data->numDeleted = 0; data->installInfo.data = data; @@ -179,13 +240,38 @@ void action_install_tickets(file_info* info, bool* populated) { data->cancelEvent = 0; - Result res = 0; - if(R_FAILED(res = util_populate_contents(&data->contents, &data->installInfo.total, info->archive, info->path, false, false, ".tik", util_filter_file_extension))) { - error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); + if(all) { + linked_list_iter iter; + linked_list_iterate(data->items, &iter); - free(data); - return; + while(linked_list_iter_has_next(&iter)) { + list_item* item = linked_list_iter_next(&iter); + file_info* info = (file_info*) item->data; + + size_t len = strlen(info->path); + if(len > 4 && strcmp(&info->path[len - 4], ".tik") == 0) { + data->installInfo.total++; + } + } + } else { + data->installInfo.total = 1; } - prompt_display("Confirmation", "Install the selected ticket(s)?", COLOR_TEXT, true, data, NULL, action_install_tickets_draw_top, action_install_tickets_onresponse); + prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_install_tickets_draw_top, action_install_tickets_onresponse); +} + +void action_install_ticket(linked_list* items, list_item* selected, file_info* target) { + action_install_tickets_internal(items, selected, target, "Install the selected ticket?", false, false); +} + +void action_install_ticket_delete(linked_list* items, list_item* selected, file_info* target) { + action_install_tickets_internal(items, selected, target, "Install and delete the selected ticket?", false, true); +} + +void action_install_tickets(linked_list* items, list_item* selected, file_info* target) { + action_install_tickets_internal(items, selected, target, "Install all tickets in the current directory?", true, false); +} + +void action_install_tickets_delete(linked_list* items, list_item* selected, file_info* target) { + action_install_tickets_internal(items, selected, target, "Install and delete all tickets in the current directory?", true, true); } \ No newline at end of file diff --git a/source/ui/section/action/launchtitle.c b/source/ui/section/action/launchtitle.c index d6e0593..ba6b6bf 100644 --- a/source/ui/section/action/launchtitle.c +++ b/source/ui/section/action/launchtitle.c @@ -1,10 +1,13 @@ #include <3ds.h> #include "action.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" +#include "../../ui.h" +#include "../../../core/screen.h" static void action_launch_title_update(ui_view* view, void* data, float* progress, char* text) { title_info* info = (title_info*) data; @@ -36,6 +39,6 @@ static void action_launch_title_onresponse(ui_view* view, void* data, bool respo } } -void action_launch_title(title_info* info, bool* populated) { - prompt_display("Confirmation", "Launch the selected title?", COLOR_TEXT, true, info, NULL, ui_draw_title_info, action_launch_title_onresponse); +void action_launch_title(linked_list* items, list_item* selected) { + prompt_display("Confirmation", "Launch the selected title?", COLOR_TEXT, true, selected->data, NULL, ui_draw_title_info, action_launch_title_onresponse); } \ No newline at end of file diff --git a/source/ui/section/action/pastefiles.c b/source/ui/section/action/pastefiles.c index 850a516..17f31d5 100644 --- a/source/ui/section/action/pastefiles.c +++ b/source/ui/section/action/pastefiles.c @@ -6,68 +6,99 @@ #include "action.h" #include "clipboard.h" +#include "../task/task.h" #include "../../error.h" #include "../../info.h" +#include "../../list.h" #include "../../prompt.h" -#include "../../../screen.h" -#include "../../../util.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { - file_info* base; - bool* populated; + linked_list* items; + file_info* target; + char** contents; + list_item* currSrc; + bool currExists; data_op_info pasteInfo; Handle cancelEvent; } paste_files_data; static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, char* dstPath) { - char baseDstPath[PATH_MAX]; - if(data->base->isDirectory) { - strncpy(baseDstPath, data->base->path, PATH_MAX); + char baseDstPath[FILE_PATH_MAX]; + if(data->target->isDirectory) { + strncpy(baseDstPath, data->target->path, FILE_PATH_MAX); } else { - util_get_parent_path(baseDstPath, data->base->path, PATH_MAX); + util_get_parent_path(baseDstPath, data->target->path, FILE_PATH_MAX); } - util_get_parent_path(dstPath, clipboard_get_path(), PATH_MAX); - snprintf(dstPath, PATH_MAX, "%s%s", baseDstPath, data->contents[index] + strlen(dstPath)); + util_get_parent_path(dstPath, clipboard_get_path(), FILE_PATH_MAX); + snprintf(dstPath, FILE_PATH_MAX, "%s%s", baseDstPath, data->contents[index] + strlen(dstPath)); } static Result action_paste_files_is_src_directory(void* data, u32 index, bool* isDirectory) { paste_files_data* pasteData = (paste_files_data*) data; - *isDirectory = util_is_dir(pasteData->base->archive, pasteData->contents[index]); + *isDirectory = util_is_dir(clipboard_get_archive(), pasteData->contents[index]); return 0; } static Result action_paste_files_make_dst_directory(void* data, u32 index) { paste_files_data* pasteData = (paste_files_data*) data; - char dstPath[PATH_MAX]; + list_item* old = pasteData->currSrc; + task_create_file_item(&pasteData->currSrc, clipboard_get_archive(), pasteData->contents[index]); + if(old != NULL) { + task_free_file(old); + } + + char dstPath[FILE_PATH_MAX]; action_paste_files_get_dst_path(pasteData, index, dstPath); + bool existed = util_exists(pasteData->target->archive, dstPath); + Result res = 0; FS_Path* fsPath = util_make_path_utf8(dstPath); if(fsPath != NULL) { - res = FSUSER_CreateDirectory(*pasteData->base->archive, *fsPath, 0); + res = FSUSER_CreateDirectory(*pasteData->target->archive, *fsPath, 0); util_free_path_utf8(fsPath); } else { res = R_FBI_OUT_OF_MEMORY; } + char parentPath[FILE_PATH_MAX]; + util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX); + + if(!existed && strcmp(parentPath, pasteData->target->path) == 0) { + list_item* dstItem = NULL; + if(R_SUCCEEDED(res) && R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath))) { + linked_list_add(pasteData->items, dstItem); + } + } + return res; } static Result action_paste_files_open_src(void* data, u32 index, u32* handle) { paste_files_data* pasteData = (paste_files_data*) data; + list_item* old = pasteData->currSrc; + task_create_file_item(&pasteData->currSrc, clipboard_get_archive(), pasteData->contents[index]); + if(old != NULL) { + task_free_file(old); + } + Result res = 0; FS_Path* fsPath = util_make_path_utf8(pasteData->contents[index]); if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, *pasteData->base->archive, *fsPath, FS_OPEN_READ, 0); + res = FSUSER_OpenFile(handle, *clipboard_get_archive(), *fsPath, FS_OPEN_READ, 0); util_free_path_utf8(fsPath); } else { @@ -92,14 +123,16 @@ static Result action_paste_files_read_src(void* data, u32 handle, u32* bytesRead static Result action_paste_files_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) { paste_files_data* pasteData = (paste_files_data*) data; - char dstPath[PATH_MAX]; + char dstPath[FILE_PATH_MAX]; action_paste_files_get_dst_path(pasteData, index, dstPath); + pasteData->currExists = util_exists(pasteData->target->archive, dstPath); + Result res = 0; FS_Path* fsPath = util_make_path_utf8(dstPath); if(fsPath != NULL) { - res = FSUSER_OpenFile(handle, *pasteData->base->archive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); + res = FSUSER_OpenFile(handle, *pasteData->target->archive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0); util_free_path_utf8(fsPath); } else { @@ -110,7 +143,26 @@ static Result action_paste_files_open_dst(void* data, u32 index, void* initialRe } static Result action_paste_files_close_dst(void* data, u32 index, bool succeeded, u32 handle) { - return FSFILE_Close(handle); + paste_files_data* pasteData = (paste_files_data*) data; + + Result res = FSFILE_Close(handle); + + if(R_SUCCEEDED(res) && !pasteData->currExists) { + char dstPath[FILE_PATH_MAX]; + action_paste_files_get_dst_path(pasteData, index, dstPath); + + char parentPath[FILE_PATH_MAX]; + util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX); + + if(strcmp(parentPath, pasteData->target->path) == 0) { + list_item* dstItem = NULL; + if(R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath))) { + linked_list_add(pasteData->items, dstItem); + } + } + } + + return res; } static Result action_paste_files_write_dst(void* data, u32 handle, u32* bytesWritten, void* buffer, u64 offset, u32 size) { @@ -121,17 +173,13 @@ static bool action_paste_files_error(void* data, u32 index, Result res) { paste_files_data* pasteData = (paste_files_data*) data; if(res == R_FBI_CANCELLED) { - prompt_display("Failure", "Paste cancelled.", COLOR_TEXT, false, pasteData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Failure", "Paste cancelled.", COLOR_TEXT, false, pasteData->target, NULL, ui_draw_file_info, NULL); return false; } else { char* path = pasteData->contents[index]; volatile bool dismissed = false; - if(strlen(path) > 48) { - error_display_res(&dismissed, pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.45s...", path); - } else { - error_display_res(&dismissed, pasteData->base, ui_draw_file_info, res, "Failed to paste content.\n%.48s", path); - } + error_display_res(&dismissed, pasteData->currSrc != NULL ? pasteData->currSrc->data : pasteData->target, ui_draw_file_info, res, "Failed to paste content.", path); while(!dismissed) { svcSleepThread(1000000); @@ -142,29 +190,50 @@ static bool action_paste_files_error(void* data, u32 index, Result res) { } static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) { - ui_draw_file_info(view, ((paste_files_data*) data)->base, x1, y1, x2, y2); + paste_files_data* pasteData = (paste_files_data*) data; + + if(pasteData->currSrc != NULL) { + ui_draw_file_info(view, ((paste_files_data*) data)->currSrc->data, x1, y1, x2, y2); + } else if(pasteData->target != NULL) { + ui_draw_file_info(view, ((paste_files_data*) data)->target, x1, y1, x2, y2); + } } static void action_paste_files_free_data(paste_files_data* data) { + if(data->currSrc != NULL) { + task_free_file(data->currSrc); + data->currSrc = NULL; + } + util_free_contents(data->contents, data->pasteInfo.total); free(data); } +static int action_paste_files_compare(const void* p1, const void* p2) { + list_item* info1 = (list_item*) p1; + list_item* info2 = (list_item*) p2; + + file_info* f1 = (file_info*) info1->data; + file_info* f2 = (file_info*) info2->data; + + return strcasecmp(f1->name, f2->name); +} + static void action_paste_files_update(ui_view* view, void* data, float* progress, char* text) { paste_files_data* pasteData = (paste_files_data*) data; if(pasteData->pasteInfo.finished) { - *pasteData->populated = false; - - if(pasteData->base->archive->id == ARCHIVE_USER_SAVEDATA) { - FSUSER_ControlArchive(*pasteData->base->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); + if(pasteData->target->archive->id == ARCHIVE_USER_SAVEDATA) { + FSUSER_ControlArchive(*pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); } + linked_list_sort(pasteData->items, action_paste_files_compare); + ui_pop(); info_destroy(view); if(!pasteData->pasteInfo.premature) { - prompt_display("Success", "Contents pasted.", COLOR_TEXT, false, pasteData->base, NULL, ui_draw_file_info, NULL); + prompt_display("Success", "Contents pasted.", COLOR_TEXT, false, pasteData->target, NULL, ui_draw_file_info, NULL); } action_paste_files_free_data(pasteData); @@ -187,16 +256,16 @@ static void action_paste_files_onresponse(ui_view* view, void* data, bool respon if(pasteData->cancelEvent != 0) { info_display("Pasting Contents", "Press B to cancel.", true, data, action_paste_files_update, action_paste_files_draw_top); } else { - error_display(NULL, pasteData->base, ui_draw_file_info, "Failed to initiate paste operation."); + error_display(NULL, pasteData->target, ui_draw_file_info, "Failed to initiate paste operation."); } } else { action_paste_files_free_data(pasteData); } } -void action_paste_contents(file_info* info, bool* populated) { +void action_paste_contents(linked_list* items, list_item* selected, file_info* target) { if(!clipboard_has_contents()) { - prompt_display("Failure", "Clipboard empty.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL); + prompt_display("Failure", "Clipboard empty.", COLOR_TEXT, false, NULL, NULL, NULL, NULL); return; } @@ -207,8 +276,10 @@ void action_paste_contents(file_info* info, bool* populated) { return; } - data->base = info; - data->populated = populated; + data->items = items; + data->target = target; + + data->currSrc = NULL; data->pasteInfo.data = data; @@ -234,7 +305,7 @@ void action_paste_contents(file_info* info, bool* populated) { Result res = 0; if(R_FAILED(res = util_populate_contents(&data->contents, &data->pasteInfo.total, clipboard_get_archive(), clipboard_get_path(), true, true, NULL, NULL))) { - error_display_res(NULL, info, ui_draw_file_info, res, "Failed to retrieve content list."); + error_display_res(NULL, data->target, ui_draw_file_info, res, "Failed to retrieve content list."); free(data); return; diff --git a/source/ui/section/dumpnand.c b/source/ui/section/dumpnand.c index f89e375..45b70a1 100644 --- a/source/ui/section/dumpnand.c +++ b/source/ui/section/dumpnand.c @@ -3,12 +3,13 @@ #include <3ds.h> -#include "task/task.h" #include "section.h" +#include "task/task.h" #include "../error.h" #include "../info.h" #include "../prompt.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/screen.h" typedef struct { data_op_info dumpInfo; diff --git a/source/ui/section/extsavedata.c b/source/ui/section/extsavedata.c index d80f094..89b0bd2 100644 --- a/source/ui/section/extsavedata.c +++ b/source/ui/section/extsavedata.c @@ -3,11 +3,14 @@ #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" static list_item browse_user_save_data = {"Browse User Save Data", COLOR_TEXT, action_browse_user_ext_save_data}; static list_item browse_spotpass_save_data = {"Browse SpotPass Save Data", COLOR_TEXT, action_browse_boss_ext_save_data}; @@ -19,12 +22,12 @@ typedef struct { } extsavedata_data; typedef struct { - ext_save_data_info* info; - bool* populated; + linked_list* items; + list_item* selected; } extsavedata_action_data; static void extsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_ext_save_data_info(view, ((extsavedata_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_ext_save_data_info(view, ((extsavedata_action_data*) data)->selected->data, x1, y1, x2, y2); } static void extsavedata_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -40,12 +43,12 @@ static void extsavedata_action_update(ui_view* view, void* data, linked_list* it } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(ext_save_data_info*, bool*) = (void(*)(ext_save_data_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected); free(data); @@ -59,7 +62,7 @@ static void extsavedata_action_update(ui_view* view, void* data, linked_list* it } } -static void extsavedata_action_open(ext_save_data_info* info, bool* populated) { +static void extsavedata_action_open(linked_list* items, list_item* selected) { extsavedata_action_data* data = (extsavedata_action_data*) calloc(1, sizeof(extsavedata_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate ext save data action data."); @@ -67,8 +70,8 @@ static void extsavedata_action_open(ext_save_data_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; list_display("Ext Save Data Action", "A: Select, B: Return", data, extsavedata_action_update, extsavedata_action_draw_top); } @@ -116,7 +119,7 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - extsavedata_action_open((ext_save_data_info*) selected->data, &listData->populated); + extsavedata_action_open(items, selected); return; } } diff --git a/source/ui/section/files.c b/source/ui/section/files.c index 296a85b..2125d61 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -1,34 +1,41 @@ -#include #include #include #include #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" -#include "../../util.h" -#include "task/task.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" +#include "../../core/util.h" -static list_item delete = {"Delete", COLOR_TEXT, action_delete_contents}; -static list_item copy = {"Copy", COLOR_TEXT, action_copy_contents}; +static list_item copy = {"Copy", COLOR_TEXT, action_copy_content}; static list_item paste = {"Paste", COLOR_TEXT, action_paste_contents}; -static list_item install_cia = {"Install CIA", COLOR_TEXT, action_install_cias}; -static list_item install_and_delete_cia = {"Install and delete CIA", COLOR_TEXT, action_install_cias_delete}; +static list_item delete_file = {"Delete", COLOR_TEXT, action_delete_contents}; -static list_item install_ticket = {"Install ticket", COLOR_TEXT, action_install_tickets}; +static list_item install_cia = {"Install CIA", COLOR_TEXT, action_install_cia}; +static list_item install_and_delete_cia = {"Install and delete CIA", COLOR_TEXT, action_install_cia_delete}; +static list_item install_ticket = {"Install ticket", COLOR_TEXT, action_install_ticket}; +static list_item install_and_delete_ticket = {"Install and delete ticket", COLOR_TEXT, action_install_ticket_delete}; + +static list_item delete_dir = {"Delete", COLOR_TEXT, action_delete_dir}; static list_item delete_all_contents = {"Delete all contents", COLOR_TEXT, action_delete_dir_contents}; +static list_item copy_all_contents = {"Copy all contents", COLOR_TEXT, action_copy_contents}; static list_item install_all_cias = {"Install all CIAs", COLOR_TEXT, action_install_cias}; static list_item install_and_delete_all_cias = {"Install and delete all CIAs", COLOR_TEXT, action_install_cias_delete}; static list_item delete_all_cias = {"Delete all CIAs", COLOR_TEXT, action_delete_dir_cias}; static list_item install_all_tickets = {"Install all tickets", COLOR_TEXT, action_install_tickets}; +static list_item install_and_delete_all_tickets = {"Install and delete all tickets", COLOR_TEXT, action_install_tickets_delete}; +static list_item delete_all_tickets = {"Delete all tickets", COLOR_TEXT, action_delete_dir_tickets}; typedef struct { Handle cancelEvent; @@ -42,12 +49,14 @@ typedef struct { } files_data; typedef struct { - file_info* info; - bool* populated; + linked_list* items; + list_item* selected; + + file_info* target; } files_action_data; static void files_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_file_info(view, ((files_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_file_info(view, ((files_action_data*) data)->target, x1, y1, x2, y2); } static void files_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -63,12 +72,12 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(file_info*, bool*) = (void(*)(file_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*, file_info*) = (void(*)(linked_list*, list_item*, file_info*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected, actionData->target); free(data); @@ -76,36 +85,43 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l } if(linked_list_size(items) == 0) { - if(actionData->info->isDirectory) { - if(actionData->info->containsCias) { + if(actionData->target->isDirectory) { + if(actionData->target->containsCias) { linked_list_add(items, &install_all_cias); linked_list_add(items, &install_and_delete_all_cias); linked_list_add(items, &delete_all_cias); } - if(actionData->info->containsTickets) { + if(actionData->target->containsTickets) { linked_list_add(items, &install_all_tickets); + linked_list_add(items, &install_and_delete_all_tickets); + linked_list_add(items, &delete_all_tickets); } linked_list_add(items, &delete_all_contents); + linked_list_add(items, ©_all_contents); + + linked_list_add(items, &delete_dir); } else { - if(actionData->info->isCia) { + if(actionData->target->isCia) { linked_list_add(items, &install_cia); linked_list_add(items, &install_and_delete_cia); } - if(actionData->info->isTicket) { + if(actionData->target->isTicket) { linked_list_add(items, &install_ticket); + linked_list_add(items, &install_and_delete_ticket); } + + linked_list_add(items, &delete_file); } - linked_list_add(items, &delete); linked_list_add(items, ©); linked_list_add(items, &paste); } } -static void files_action_open(file_info* info, bool* populated) { +static void files_action_open(linked_list* items, list_item* selected, file_info* target) { files_action_data* data = (files_action_data*) calloc(1, sizeof(files_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate files action data."); @@ -113,10 +129,12 @@ static void files_action_open(file_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; - list_display(info->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", data, files_action_update, files_action_draw_top); + data->target = target; + + list_display(target->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", data, files_action_update, files_action_draw_top); } static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { @@ -136,15 +154,15 @@ static void files_repopulate(files_data* listData, linked_list* items) { } while(!util_is_dir(&listData->archive, listData->currDir.path)) { - char parentPath[PATH_MAX]; + char parentPath[FILE_PATH_MAX]; - util_get_parent_path(parentPath, listData->currDir.path, PATH_MAX); - strncpy(listData->currDir.path, parentPath, PATH_MAX); - util_get_path_file(listData->currDir.name, listData->currDir.path, NAME_MAX); + util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX); + strncpy(listData->currDir.path, parentPath, FILE_PATH_MAX); + util_get_path_file(listData->currDir.name, listData->currDir.path, FILE_NAME_MAX); - util_get_parent_path(parentPath, listData->currDir.path, PATH_MAX); - strncpy(listData->parentDir.path, parentPath, PATH_MAX); - util_get_path_file(listData->parentDir.name, listData->parentDir.path, NAME_MAX); + util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX); + strncpy(listData->parentDir.path, parentPath, FILE_PATH_MAX); + util_get_path_file(listData->parentDir.name, listData->parentDir.path, FILE_NAME_MAX); } listData->cancelEvent = task_populate_files(items, &listData->currDir); @@ -152,13 +170,13 @@ static void files_repopulate(files_data* listData, linked_list* items) { } static void files_navigate(files_data* listData, linked_list* items, const char* path) { - strncpy(listData->currDir.path, path, PATH_MAX); - util_get_path_file(listData->currDir.name, listData->currDir.path, NAME_MAX); + strncpy(listData->currDir.path, path, FILE_PATH_MAX); + util_get_path_file(listData->currDir.name, listData->currDir.path, FILE_NAME_MAX); - char parentPath[PATH_MAX]; - util_get_parent_path(parentPath, listData->currDir.path, PATH_MAX); - strncpy(listData->parentDir.path, parentPath, PATH_MAX); - util_get_path_file(listData->parentDir.name, listData->parentDir.path, NAME_MAX); + char parentPath[FILE_PATH_MAX]; + util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX); + strncpy(listData->parentDir.path, parentPath, FILE_PATH_MAX); + util_get_path_file(listData->parentDir.name, listData->parentDir.path, FILE_NAME_MAX); files_repopulate(listData, items); } @@ -200,7 +218,7 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite } if(hidKeysDown() & KEY_Y) { - files_action_open(&listData->currDir, &listData->populated); + files_action_open(items, selected, &listData->currDir); return; } @@ -210,7 +228,7 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite if(util_is_dir(&listData->archive, fileInfo->path)) { files_navigate(listData, items, fileInfo->path); } else { - files_action_open(fileInfo, &listData->populated); + files_action_open(items, selected, fileInfo); return; } } @@ -258,8 +276,8 @@ void files_open(FS_Archive archive) { } data->currDir.archive = &data->archive; - snprintf(data->currDir.path, PATH_MAX, "/"); - util_get_path_file(data->currDir.name, data->currDir.path, NAME_MAX); + snprintf(data->currDir.path, FILE_PATH_MAX, "/"); + util_get_path_file(data->currDir.name, data->currDir.path, FILE_NAME_MAX); data->currDir.isDirectory = true; data->currDir.containsCias = false; data->currDir.size = 0; diff --git a/source/ui/section/networkinstall.c b/source/ui/section/networkinstall.c index 0c8a1d7..c4ef504 100644 --- a/source/ui/section/networkinstall.c +++ b/source/ui/section/networkinstall.c @@ -7,12 +7,13 @@ #include <3ds.h> -#include "task/task.h" #include "section.h" +#include "task/task.h" #include "../error.h" #include "../info.h" #include "../prompt.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/screen.h" typedef struct { int serverSocket; diff --git a/source/ui/section/pendingtitles.c b/source/ui/section/pendingtitles.c index 3e86b27..bd45bef 100644 --- a/source/ui/section/pendingtitles.c +++ b/source/ui/section/pendingtitles.c @@ -3,11 +3,14 @@ #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" static list_item delete_pending_title = {"Delete Pending Title", COLOR_TEXT, action_delete_pending_title}; static list_item delete_all_pending_titles = {"Delete All Pending Titles", COLOR_TEXT, action_delete_all_pending_titles}; @@ -18,12 +21,12 @@ typedef struct { } pendingtitles_data; typedef struct { - pending_title_info* info; - bool* populated; + linked_list* items; + list_item* selected; } pendingtitles_action_data; static void pendingtitles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_pending_title_info(view, ((pendingtitles_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_pending_title_info(view, ((pendingtitles_action_data*) data)->selected->data, x1, y1, x2, y2); } static void pendingtitles_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -39,12 +42,12 @@ static void pendingtitles_action_update(ui_view* view, void* data, linked_list* } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(pending_title_info*, bool*) = (void(*)(pending_title_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected); free(data); @@ -57,7 +60,7 @@ static void pendingtitles_action_update(ui_view* view, void* data, linked_list* } } -static void pendingtitles_action_open(pending_title_info* info, bool* populated) { +static void pendingtitles_action_open(linked_list* items, list_item* selected) { pendingtitles_action_data* data = (pendingtitles_action_data*) calloc(1, sizeof(pendingtitles_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate pending titles action data."); @@ -65,8 +68,8 @@ static void pendingtitles_action_open(pending_title_info* info, bool* populated) return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; list_display("Pending Title Action", "A: Select, B: Return", data, pendingtitles_action_update, pendingtitles_action_draw_top); } @@ -114,7 +117,7 @@ static void pendingtitles_update(ui_view* view, void* data, linked_list* items, } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - pendingtitles_action_open((pending_title_info*) selected->data, &listData->populated); + pendingtitles_action_open(items, selected); return; } } diff --git a/source/ui/section/qrinstall.c b/source/ui/section/qrinstall.c index 1b10501..2c8eaa5 100644 --- a/source/ui/section/qrinstall.c +++ b/source/ui/section/qrinstall.c @@ -4,12 +4,13 @@ #include <3ds.h> -#include "task/task.h" #include "section.h" +#include "task/task.h" #include "../error.h" #include "../info.h" #include "../prompt.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/screen.h" #include "../../quirc/quirc_internal.h" #define IMAGE_WIDTH 400 diff --git a/source/ui/section/section.h b/source/ui/section/section.h index b6297d1..ea28975 100644 --- a/source/ui/section/section.h +++ b/source/ui/section/section.h @@ -1,7 +1,5 @@ #pragma once -#include "../ui.h" - void dumpnand_open(); void extsavedata_open(); void files_open(FS_Archive archive); diff --git a/source/ui/section/systemsavedata.c b/source/ui/section/systemsavedata.c index 52e15e3..95d4320 100644 --- a/source/ui/section/systemsavedata.c +++ b/source/ui/section/systemsavedata.c @@ -3,11 +3,14 @@ #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" static list_item browse_save_data = {"Browse Save Data", COLOR_TEXT, action_browse_system_save_data}; static list_item delete_save_data = {"Delete Save Data", COLOR_TEXT, action_delete_system_save_data}; @@ -18,12 +21,12 @@ typedef struct { } systemsavedata_data; typedef struct { - system_save_data_info* info; - bool* populated; + linked_list* items; + list_item* selected; } systemsavedata_action_data; static void systemsavedata_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_system_save_data_info(view, ((systemsavedata_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_system_save_data_info(view, ((systemsavedata_action_data*) data)->selected->data, x1, y1, x2, y2); } static void systemsavedata_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -39,12 +42,12 @@ static void systemsavedata_action_update(ui_view* view, void* data, linked_list* } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(system_save_data_info*, bool*) = (void(*)(system_save_data_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected); free(data); @@ -57,7 +60,7 @@ static void systemsavedata_action_update(ui_view* view, void* data, linked_list* } } -static void systemsavedata_action_open(system_save_data_info* info, bool* populated) { +static void systemsavedata_action_open(linked_list* items, list_item* selected) { systemsavedata_action_data* data = (systemsavedata_action_data*) calloc(1, sizeof(systemsavedata_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate system save data action data."); @@ -65,8 +68,8 @@ static void systemsavedata_action_open(system_save_data_info* info, bool* popula return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; list_display("System Save Data Action", "A: Select, B: Return", data, systemsavedata_action_update, systemsavedata_action_draw_top); } @@ -114,7 +117,7 @@ static void systemsavedata_update(ui_view* view, void* data, linked_list* items, } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - systemsavedata_action_open((system_save_data_info*) selected->data, &listData->populated); + systemsavedata_action_open(items, selected); return; } } diff --git a/source/ui/section/task/capturecam.c b/source/ui/section/task/capturecam.c index a447b13..cf3751f 100644 --- a/source/ui/section/task/capturecam.c +++ b/source/ui/section/task/capturecam.c @@ -3,9 +3,9 @@ #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "task.h" #define EVENT_CANCEL 0 #define EVENT_RECV 1 @@ -54,6 +54,8 @@ static void task_capture_cam_thread(void* arg) { && R_SUCCEEDED(res = CAMU_StartCapture(PORT_CAM1))) { bool cancelRequested = false; while(!task_is_quit_all() && !cancelRequested && R_SUCCEEDED(res)) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); + s32 index = 0; if(R_SUCCEEDED(res = svcWaitSynchronizationN(&index, events, EVENT_COUNT, false, U64_MAX))) { switch(index) { @@ -155,7 +157,7 @@ Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height) { return 0; } - if(threadCreate(task_capture_cam_thread, data, 0x4000, 0x19, 1, true) == NULL) { + if(threadCreate(task_capture_cam_thread, data, 0x10000, 0x19, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create camera capture thread."); svcCloseHandle(data->mutex); diff --git a/source/ui/section/task/dataop.c b/source/ui/section/task/dataop.c index 07f1df9..7e148e4 100644 --- a/source/ui/section/task/dataop.c +++ b/source/ui/section/task/dataop.c @@ -3,9 +3,9 @@ #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "task.h" typedef struct { data_op_info* info; @@ -41,6 +41,7 @@ static bool task_data_op_copy(data_op_data* data, u32 index) { bool firstRun = true; while(data->info->currProcessed < data->info->currTotal) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { res = R_FBI_CANCELLED; break; @@ -177,7 +178,7 @@ Handle task_data_op(data_op_info* info) { return 0; } - if(threadCreate(task_data_op_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create data operation thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listextsavedata.c b/source/ui/section/task/listextsavedata.c index 2a448e0..0019ffb 100644 --- a/source/ui/section/task/listextsavedata.c +++ b/source/ui/section/task/listextsavedata.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,11 +5,12 @@ #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../screen.h" -#include "../../../util.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" #define MAX_EXT_SAVE_DATA 512 @@ -24,83 +24,78 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data Result res = 0; u32 extSaveDataCount = 0; - u64* extSaveDataIds = (u64*) calloc(MAX_EXT_SAVE_DATA, sizeof(u64)); - if(extSaveDataIds != NULL) { - if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, MAX_EXT_SAVE_DATA, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) { - qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), util_compare_u64); + u64 extSaveDataIds[MAX_EXT_SAVE_DATA]; + if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, MAX_EXT_SAVE_DATA, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) { + qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), util_compare_u64); - SMDH smdh; - for(u32 i = 0; i < extSaveDataCount && R_SUCCEEDED(res); i++) { - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } + SMDH smdh; + for(u32 i = 0; i < extSaveDataCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - 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; - extSaveDataInfo->hasMeta = false; + list_item* item = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + 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; + extSaveDataInfo->hasMeta = false; - 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); + 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); + utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, LIST_ITEM_NAME_MAX - 1); - extSaveDataInfo->hasMeta = true; - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->meta.publisher) - 1); - extSaveDataInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, 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->color = COLOR_NAND; - } else if(mediaType == MEDIATYPE_SD) { - item->color = COLOR_SD; - } - - item->data = extSaveDataInfo; - - linked_list_add(data->items, item); - } else { - free(item); - - res = R_FBI_OUT_OF_MEMORY; + extSaveDataInfo->hasMeta = true; + utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->meta.shortDescription) - 1); + utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->meta.longDescription) - 1); + utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->meta.publisher) - 1); + extSaveDataInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, 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, LIST_ITEM_NAME_MAX, "%016llX", extSaveDataIds[i]); + } + + if(mediaType == MEDIATYPE_NAND) { + item->color = COLOR_NAND; + } else if(mediaType == MEDIATYPE_SD) { + item->color = COLOR_SD; + } + + item->data = extSaveDataInfo; + + linked_list_add(data->items, item); } else { + free(item); + res = R_FBI_OUT_OF_MEMORY; } + } else { + res = R_FBI_OUT_OF_MEMORY; } } - - free(extSaveDataIds); - } else { - res = R_FBI_OUT_OF_MEMORY; } return res; @@ -118,6 +113,23 @@ static void task_populate_ext_save_data_thread(void* arg) { free(data); } +void task_free_ext_save_data(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) item->data; + if(extSaveDataInfo->hasMeta) { + screen_unload_texture(extSaveDataInfo->meta.texture); + } + + free(item->data); + } + + free(item); +} + void task_clear_ext_save_data(linked_list* items) { if(items == NULL) { return; @@ -128,18 +140,7 @@ void task_clear_ext_save_data(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - ext_save_data_info* extSaveDataInfo = (ext_save_data_info*) item->data; - if(extSaveDataInfo->hasMeta) { - screen_unload_texture(extSaveDataInfo->meta.texture); - } - - free(item->data); - } - - free(item); - + task_free_ext_save_data(item); linked_list_iter_remove(&iter); } } @@ -168,7 +169,7 @@ Handle task_populate_ext_save_data(linked_list* items) { return 0; } - if(threadCreate(task_populate_ext_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create ext save data list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listfiles.c b/source/ui/section/task/listfiles.c index 492c8ee..d62c3cf 100644 --- a/source/ui/section/task/listfiles.c +++ b/source/ui/section/task/listfiles.c @@ -1,4 +1,3 @@ -#include #include #include #include @@ -6,11 +5,12 @@ #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../util.h" -#include "../../../screen.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" #define MAX_FILES 1024 @@ -21,10 +21,114 @@ typedef struct { Handle cancelEvent; } populate_files_data; +Result task_create_file_item(list_item** out, FS_Archive* archive, const char* path) { + Result res = 0; + + list_item* item = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + file_info* fileInfo = (file_info*) calloc(1, sizeof(file_info)); + if(fileInfo != NULL) { + fileInfo->archive = archive; + util_get_path_file(fileInfo->name, path, FILE_NAME_MAX); + fileInfo->containsCias = false; + fileInfo->size = 0; + fileInfo->isCia = false; + + if(util_is_dir(archive, path)) { + item->color = COLOR_DIRECTORY; + + size_t len = strlen(path); + if(len > 1 && path[len - 1] != '/') { + snprintf(fileInfo->path, FILE_PATH_MAX, "%s/", path); + } else { + strncpy(fileInfo->path, path, FILE_PATH_MAX); + } + + fileInfo->isDirectory = true; + } else { + item->color = COLOR_TEXT; + + strncpy(fileInfo->path, path, FILE_PATH_MAX); + fileInfo->isDirectory = false; + + FS_Path* fileFsPath = util_make_path_utf8(fileInfo->path); + if(fileFsPath != NULL) { + Handle fileHandle; + if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *archive, *fileFsPath, FS_OPEN_READ, 0))) { + FSFILE_GetSize(fileHandle, &fileInfo->size); + + size_t len = strlen(fileInfo->path); + if(len > 4) { + if(strcasecmp(&fileInfo->path[len - 4], ".cia") == 0) { + AM_TitleEntry titleEntry; + if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) { + fileInfo->isCia = true; + fileInfo->ciaInfo.titleId = titleEntry.titleID; + fileInfo->ciaInfo.version = titleEntry.version; + fileInfo->ciaInfo.installedSize = titleEntry.size; + fileInfo->ciaInfo.hasMeta = false; + + if(((titleEntry.titleID >> 32) & 0x8010) != 0 && R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) { + fileInfo->ciaInfo.installedSize = titleEntry.size; + } + + SMDH smdh; + if(R_SUCCEEDED(AM_GetCiaIcon(&smdh, fileHandle))) { + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); + + fileInfo->ciaInfo.hasMeta = true; + utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription) - 1); + utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription) - 1); + utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.meta.publisher) - 1); + fileInfo->ciaInfo.meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); + } + } + } else if(strcasecmp(&fileInfo->path[len - 4], ".tik") == 0) { + u32 bytesRead = 0; + + u8 sigType = 0; + if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 3, &sigType, sizeof(sigType))) && bytesRead == sizeof(sigType) && sigType <= 5) { + static u32 dataOffsets[6] = {0x240, 0x140, 0x80, 0x240, 0x140, 0x80}; + static u32 titleIdOffset = 0x9C; + + u64 titleId = 0; + if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, dataOffsets[sigType] + titleIdOffset, &titleId, sizeof(titleId))) && bytesRead == sizeof(titleId)) { + fileInfo->isTicket = true; + fileInfo->ticketInfo.titleId = __builtin_bswap64(titleId); + } + } + } + } + + FSFILE_Close(fileHandle); + } + + util_free_path_utf8(fileFsPath); + } + } + + strncpy(item->name, fileInfo->name, LIST_ITEM_NAME_MAX); + item->data = fileInfo; + + *out = item; + } else { + free(item); + + res = R_FBI_OUT_OF_MEMORY; + } + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + return res; +} + static void task_populate_files_thread(void* arg) { populate_files_data* data = (populate_files_data*) arg; data->dir->containsCias = false; + data->dir->containsTickets = false; Result res = 0; @@ -38,8 +142,8 @@ static void task_populate_files_thread(void* arg) { if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries)) && entryCount > 0) { qsort(entries, entryCount, sizeof(FS_DirectoryEntry), util_compare_directory_entries); - SMDH smdh; for(u32 i = 0; i < entryCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } @@ -48,98 +152,21 @@ static void task_populate_files_thread(void* arg) { continue; } - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - file_info* fileInfo = (file_info*) calloc(1, sizeof(file_info)); - if(fileInfo != NULL) { - fileInfo->archive = data->dir->archive; - utf16_to_utf8((uint8_t*) fileInfo->name, entries[i].name, NAME_MAX - 1); - fileInfo->containsCias = false; - fileInfo->size = 0; - fileInfo->isCia = false; + char name[FILE_NAME_MAX] = {'\0'}; + utf16_to_utf8((uint8_t*) name, entries[i].name, FILE_NAME_MAX - 1); - if(entries[i].attributes & FS_ATTRIBUTE_DIRECTORY) { - item->color = COLOR_DIRECTORY; + char path[FILE_PATH_MAX] = {'\0'}; + snprintf(path, FILE_PATH_MAX, "%s%s", data->dir->path, name); - snprintf(fileInfo->path, PATH_MAX, "%s%s/", data->dir->path, fileInfo->name); - fileInfo->isDirectory = true; - } else { - item->color = COLOR_TEXT; - - snprintf(fileInfo->path, PATH_MAX, "%s%s", data->dir->path, fileInfo->name); - fileInfo->isDirectory = false; - - FS_Path* fileFsPath = util_make_path_utf8(fileInfo->path); - if(fileFsPath != NULL) { - Handle fileHandle; - if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *data->dir->archive, *fileFsPath, FS_OPEN_READ, 0))) { - FSFILE_GetSize(fileHandle, &fileInfo->size); - - size_t len = strlen(fileInfo->path); - if(len > 4) { - if(strcasecmp(&fileInfo->path[len - 4], ".cia") == 0) { - AM_TitleEntry titleEntry; - if(R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_SD, &titleEntry, fileHandle))) { - data->dir->containsCias = true; - - fileInfo->isCia = true; - fileInfo->ciaInfo.titleId = titleEntry.titleID; - fileInfo->ciaInfo.version = titleEntry.version; - fileInfo->ciaInfo.installedSize = titleEntry.size; - fileInfo->ciaInfo.hasMeta = false; - - if(((titleEntry.titleID >> 32) & 0x8010) != 0 && R_SUCCEEDED(AM_GetCiaFileInfo(MEDIATYPE_NAND, &titleEntry, fileHandle))) { - fileInfo->ciaInfo.installedSize = titleEntry.size; - } - - if(R_SUCCEEDED(AM_GetCiaIcon(&smdh, fileHandle))) { - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); - - fileInfo->ciaInfo.hasMeta = true; - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.meta.publisher) - 1); - fileInfo->ciaInfo.meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); - } - } - } else if(strcasecmp(&fileInfo->path[len - 4], ".tik") == 0) { - u32 bytesRead = 0; - - u8 sigType = 0; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 3, &sigType, sizeof(sigType))) && bytesRead == sizeof(sigType) && sigType <= 5) { - static u32 dataOffsets[6] = {0x240, 0x140, 0x80, 0x240, 0x140, 0x80}; - static u32 titleIdOffset = 0x9C; - - u64 titleId = 0; - if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, dataOffsets[sigType] + titleIdOffset, &titleId, sizeof(titleId))) && bytesRead == sizeof(titleId)) { - data->dir->containsTickets = true; - - fileInfo->isTicket = true; - fileInfo->ticketInfo.titleId = __builtin_bswap64(titleId); - } - } - } - } - - FSFILE_Close(fileHandle); - } - - util_free_path_utf8(fileFsPath); - } - } - - strncpy(item->name, fileInfo->name, NAME_MAX); - item->data = fileInfo; - - linked_list_add(data->items, item); - } else { - free(item); - - res = R_FBI_OUT_OF_MEMORY; + list_item* item = NULL; + if(R_SUCCEEDED(res = task_create_file_item(&item, data->dir->archive, path))) { + if(((file_info*) item->data)->isCia) { + data->dir->containsCias = true; + } else if(((file_info*) item->data)->isTicket) { + data->dir->containsTickets = true; } - } else { - res = R_FBI_OUT_OF_MEMORY; + + linked_list_add(data->items, item); } } } @@ -165,6 +192,23 @@ static void task_populate_files_thread(void* arg) { free(data); } +void task_free_file(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + file_info* fileInfo = (file_info*) item->data; + if(fileInfo->isCia && fileInfo->ciaInfo.hasMeta) { + screen_unload_texture(fileInfo->ciaInfo.meta.texture); + } + + free(item->data); + } + + free(item); +} + void task_clear_files(linked_list* items) { if(items == NULL) { return; @@ -175,18 +219,7 @@ void task_clear_files(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - file_info* fileInfo = (file_info*) item->data; - if(fileInfo->isCia && fileInfo->ciaInfo.hasMeta) { - screen_unload_texture(fileInfo->ciaInfo.meta.texture); - } - - free(item->data); - } - - free(item); - + task_free_file(item); linked_list_iter_remove(&iter); } } @@ -216,7 +249,7 @@ Handle task_populate_files(linked_list* items, file_info* dir) { return 0; } - if(threadCreate(task_populate_files_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_files_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create file list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listpendingtitles.c b/source/ui/section/task/listpendingtitles.c index 06126db..ef1a7d4 100644 --- a/source/ui/section/task/listpendingtitles.c +++ b/source/ui/section/task/listpendingtitles.c @@ -1,15 +1,15 @@ -#include #include #include #include #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../screen.h" -#include "../../../util.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { linked_list* items; @@ -31,6 +31,7 @@ static Result task_populate_pending_titles_from(populate_pending_titles_data* da if(pendingTitleInfos != NULL) { if(R_SUCCEEDED(res = AM_GetPendingTitleInfo(pendingTitleCount, mediaType, pendingTitleIds, pendingTitleInfos))) { for(u32 i = 0; i < pendingTitleCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } @@ -43,7 +44,7 @@ static Result task_populate_pending_titles_from(populate_pending_titles_data* da pendingTitleInfo->titleId = pendingTitleIds[i]; pendingTitleInfo->version = pendingTitleInfos[i].version; - snprintf(item->name, NAME_MAX, "%016llX", pendingTitleIds[i]); + snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", pendingTitleIds[i]); if(mediaType == MEDIATYPE_NAND) { item->color = COLOR_NAND; } else if(mediaType == MEDIATYPE_SD) { @@ -91,6 +92,18 @@ static void task_populate_pending_titles_thread(void* arg) { free(data); } +void task_free_pending_title(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + free(item->data); + } + + free(item); +} + void task_clear_pending_titles(linked_list* items) { if(items == NULL) { return; @@ -101,13 +114,7 @@ void task_clear_pending_titles(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - free(item->data); - } - - free(item); - + task_free_pending_title(item); linked_list_iter_remove(&iter); } } @@ -136,7 +143,7 @@ Handle task_populate_pending_titles(linked_list* items) { return 0; } - if(threadCreate(task_populate_pending_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create pending title list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listsystemsavedata.c b/source/ui/section/task/listsystemsavedata.c index 169d1e8..ca422f3 100644 --- a/source/ui/section/task/listsystemsavedata.c +++ b/source/ui/section/task/listsystemsavedata.c @@ -1,16 +1,15 @@ -#include #include #include #include -#include #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../screen.h" -#include "../../../util.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" #define MAX_SYSTEM_SAVE_DATA 512 @@ -26,41 +25,36 @@ static void task_populate_system_save_data_thread(void* arg) { Result res = 0; u32 systemSaveDataCount = 0; - u32* systemSaveDataIds = (u32*) calloc(MAX_SYSTEM_SAVE_DATA, sizeof(u32)); - if(systemSaveDataIds != NULL) { - if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, MAX_SYSTEM_SAVE_DATA * sizeof(u32), systemSaveDataIds))) { - qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u32); + u32 systemSaveDataIds[MAX_SYSTEM_SAVE_DATA]; + if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, MAX_SYSTEM_SAVE_DATA * sizeof(u32), systemSaveDataIds))) { + qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u32); - for(u32 i = 0; i < systemSaveDataCount && R_SUCCEEDED(res); i++) { - if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { - break; - } + for(u32 i = 0; i < systemSaveDataCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); + if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { + break; + } - list_item* item = (list_item*) calloc(1, sizeof(list_item)); - if(item != NULL) { - 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 = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + system_save_data_info* systemSaveDataInfo = (system_save_data_info*) calloc(1, sizeof(system_save_data_info)); + if(systemSaveDataInfo != NULL) { + systemSaveDataInfo->systemSaveDataId = systemSaveDataIds[i]; - snprintf(item->name, NAME_MAX, "%08lX", systemSaveDataIds[i]); - item->color = COLOR_TEXT; - item->data = systemSaveDataInfo; + snprintf(item->name, LIST_ITEM_NAME_MAX, "%08lX", systemSaveDataIds[i]); + item->color = COLOR_TEXT; + item->data = systemSaveDataInfo; - linked_list_add(data->items, item); - } else { - free(item); - - res = R_FBI_OUT_OF_MEMORY; - } + linked_list_add(data->items, item); } else { + free(item); + res = R_FBI_OUT_OF_MEMORY; } + } else { + res = R_FBI_OUT_OF_MEMORY; } } - - free(systemSaveDataIds); - } else { - res = R_FBI_OUT_OF_MEMORY; } if(R_FAILED(res)) { @@ -71,6 +65,18 @@ static void task_populate_system_save_data_thread(void* arg) { free(data); } +void task_free_system_save_data(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + free(item->data); + } + + free(item); +} + void task_clear_system_save_data(linked_list* items) { if(items == NULL) { return; @@ -81,13 +87,7 @@ void task_clear_system_save_data(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - free(item->data); - } - - free(item); - + task_free_system_save_data(item); linked_list_iter_remove(&iter); } } @@ -116,7 +116,7 @@ Handle task_populate_system_save_data(linked_list* items) { return 0; } - if(threadCreate(task_populate_system_save_data_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create system save data list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listtickets.c b/source/ui/section/task/listtickets.c index d588afd..25a9157 100644 --- a/source/ui/section/task/listtickets.c +++ b/source/ui/section/task/listtickets.c @@ -1,16 +1,15 @@ -#include #include #include #include -#include #include <3ds.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../screen.h" -#include "../../../util.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { linked_list* items; @@ -31,6 +30,7 @@ static void task_populate_tickets_thread(void* arg) { qsort(ticketIds, ticketCount, sizeof(u64), util_compare_u64); for(u32 i = 0; i < ticketCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } @@ -41,7 +41,7 @@ static void task_populate_tickets_thread(void* arg) { if(ticketInfo != NULL) { ticketInfo->titleId = ticketIds[i]; - snprintf(item->name, NAME_MAX, "%016llX", ticketIds[i]); + snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", ticketIds[i]); item->color = COLOR_TEXT; item->data = ticketInfo; @@ -71,6 +71,18 @@ static void task_populate_tickets_thread(void* arg) { free(data); } +void task_free_ticket(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + free(item->data); + } + + free(item); +} + void task_clear_tickets(linked_list* items) { if(items == NULL) { return; @@ -81,13 +93,7 @@ void task_clear_tickets(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - free(item->data); - } - - free(item); - + task_free_ticket(item); linked_list_iter_remove(&iter); } } @@ -116,7 +122,7 @@ Handle task_populate_tickets(linked_list* items) { return 0; } - if(threadCreate(task_populate_tickets_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create ticket list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c index c51c8d6..19748b2 100644 --- a/source/ui/section/task/listtitles.c +++ b/source/ui/section/task/listtitles.c @@ -5,13 +5,13 @@ #include #include <3ds.h> -#include <3ds/services/am.h> +#include "task.h" #include "../../list.h" #include "../../error.h" -#include "../../../util.h" -#include "../../../screen.h" -#include "task.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" typedef struct { linked_list* items; @@ -43,26 +43,23 @@ static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaT Handle fileHandle; if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) { - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - 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') { - titleInfo->hasMeta = true; + SMDH smdh; - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); + u32 bytesRead = 0; + 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') { + titleInfo->hasMeta = true; - utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX - 1); + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); - utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1); - utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1); - titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false); - } + utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1); + + utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1); + utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1); + utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1); + titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false); } - - free(smdh); } FSFILE_Close(fileHandle); @@ -126,18 +123,20 @@ static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaT version = entry.version; installedSize = entry.size; } else { - u8* header = (u8*) calloc(1, 0x3B4); - if(header != NULL) { - if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(mediaType, titleId, header))) { - realTitleId = titleId != 0 ? titleId : *(u64*) &header[0x230]; - memcpy(productCode, header, 0x00C); - version = header[0x01E]; - installedSize = (header[0x012] & 0x2) != 0 ? *(u32*) &header[0x210] : *(u32*) &header[0x080]; + u8 header[0x3B4] = {0}; + if(R_SUCCEEDED(res = FSUSER_GetLegacyRomHeader(mediaType, titleId, header))) { + memcpy(&realTitleId, &header[0x230], sizeof(u64)); + memcpy(productCode, header, 0x00C); + version = header[0x01E]; + + u32 size = 0; + if((header[0x012] & 0x2) != 0) { + memcpy(&size, &header[0x210], sizeof(u32)); + } else { + memcpy(&size, &header[0x080], sizeof(u32)); } - free(header); - } else { - res = R_FBI_OUT_OF_MEMORY; + installedSize = size; } } @@ -154,63 +153,59 @@ static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaT titleInfo->twl = true; titleInfo->hasMeta = false; - BNR* bnr = (BNR*) calloc(1, sizeof(BNR)); - if(bnr != NULL) { - if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) { - titleInfo->hasMeta = true; + BNR bnr; + if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) &bnr))) { + titleInfo->hasMeta = true; - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); - char title[0x100] = {'\0'}; - utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], sizeof(title) - 1); + char title[0x100] = {'\0'}; + utf16_to_utf8((uint8_t*) title, bnr.titles[systemLanguage], sizeof(title) - 1); - if(strchr(title, '\n') == NULL) { - size_t len = strlen(title); - strncpy(item->name, title, len); - strncpy(titleInfo->meta.shortDescription, title, len); - } else { - char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher}; - int currDest = 0; + if(strchr(title, '\n') == NULL) { + size_t len = strlen(title); + strncpy(item->name, title, len); + strncpy(titleInfo->meta.shortDescription, title, len); + } else { + char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher}; + int currDest = 0; - char* last = title; - char* curr = NULL; + char* last = title; + char* curr = NULL; - while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) { - strncpy(destinations[currDest++], last, curr - last); - last = curr + 1; - *curr = ' '; - } - - strncpy(item->name, title, last - title); - if(currDest < 3) { - strncpy(destinations[currDest], last, strlen(title) - (last - title)); - } + while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) { + strncpy(destinations[currDest++], last, curr - last); + last = curr + 1; + *curr = ' '; } - u8 icon[32 * 32 * 2]; - for(u32 x = 0; x < 32; x++) { - for(u32 y = 0; y < 32; y++) { - u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1); - u32 srcShift = (x & 1) * 4; - u16 srcPx = bnr->mainIconPalette[(bnr->mainIconBitmap[srcPos] >> srcShift) & 0xF]; - - u8 r = (u8) (srcPx & 0x1F); - u8 g = (u8) ((srcPx >> 5) & 0x1F); - u8 b = (u8) ((srcPx >> 10) & 0x1F); - - u16 reversedPx = (u16) ((r << 11) | (g << 6) | (b << 1) | 1); - - u32 dstPos = (y * 32 + x) * 2; - icon[dstPos + 0] = (u8) (reversedPx & 0xFF); - icon[dstPos + 1] = (u8) ((reversedPx >> 8) & 0xFF); - } + strncpy(item->name, title, last - title); + if(currDest < 3) { + strncpy(destinations[currDest], last, strlen(title) - (last - title)); } - - titleInfo->meta.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false); } - free(bnr); + u8 icon[32 * 32 * 2]; + for(u32 x = 0; x < 32; x++) { + for(u32 y = 0; y < 32; y++) { + u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1); + u32 srcShift = (x & 1) * 4; + u16 srcPx = bnr.mainIconPalette[(bnr.mainIconBitmap[srcPos] >> srcShift) & 0xF]; + + u8 r = (u8) (srcPx & 0x1F); + u8 g = (u8) ((srcPx >> 5) & 0x1F); + u8 b = (u8) ((srcPx >> 10) & 0x1F); + + u16 reversedPx = (u16) ((r << 11) | (g << 6) | (b << 1) | 1); + + u32 dstPos = (y * 32 + x) * 2; + icon[dstPos + 0] = (u8) (reversedPx & 0xFF); + icon[dstPos + 1] = (u8) ((reversedPx >> 8) & 0xFF); + } + } + + titleInfo->meta.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false); } bool empty = strlen(item->name) == 0; @@ -267,6 +262,7 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType qsort(titleIds, titleCount, sizeof(u64), util_compare_u64); for(u32 i = 0; i < titleCount && R_SUCCEEDED(res); i++) { + svcWaitSynchronization(task_get_pause_event(), U64_MAX); if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) { break; } @@ -304,6 +300,23 @@ static void task_populate_titles_thread(void* arg) { free(data); } +void task_free_title(list_item* item) { + if(item == NULL) { + return; + } + + if(item->data != NULL) { + title_info* titleInfo = (title_info*) item->data; + if(titleInfo->hasMeta) { + screen_unload_texture(titleInfo->meta.texture); + } + + free(item->data); + } + + free(item); +} + void task_clear_titles(linked_list* items) { if(items == NULL) { return; @@ -314,18 +327,7 @@ void task_clear_titles(linked_list* items) { while(linked_list_iter_has_next(&iter)) { list_item* item = (list_item*) linked_list_iter_next(&iter); - - if(item->data != NULL) { - title_info* titleInfo = (title_info*) item->data; - if(titleInfo->hasMeta) { - screen_unload_texture(titleInfo->meta.texture); - } - - free(item->data); - } - - free(item); - + task_free_title(item); linked_list_iter_remove(&iter); } } @@ -354,7 +356,7 @@ Handle task_populate_titles(linked_list* items) { return 0; } - if(threadCreate(task_populate_titles_thread, data, 0x4000, 0x18, 1, true) == NULL) { + if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) { error_display(NULL, NULL, NULL, "Failed to create title list thread."); svcCloseHandle(data->cancelEvent); diff --git a/source/ui/section/task/task.c b/source/ui/section/task/task.c index e7b0808..1671e34 100644 --- a/source/ui/section/task/task.c +++ b/source/ui/section/task/task.c @@ -1,13 +1,58 @@ #include <3ds.h> #include "task.h" +#include "../../../core/util.h" static bool task_quit; +static Handle task_pause_event; + +static aptHookCookie cookie; + +static void task_apt_hook(APT_HookType hook, void* param) { + switch(hook) { + case APTHOOK_ONRESTORE: + case APTHOOK_ONWAKEUP: + svcSignalEvent(task_pause_event); + break; + case APTHOOK_ONSUSPEND: + case APTHOOK_ONSLEEP: + svcClearEvent(task_pause_event); + break; + default: + break; + } +} + +void task_init() { + task_quit = false; + + Result res = 0; + if(R_FAILED(res = svcCreateEvent(&task_pause_event, 1))) { + util_panic("Failed to create task awake event: 0x%08lX", res); + return; + } + + svcSignalEvent(task_pause_event); + + aptHook(&cookie, task_apt_hook, NULL); +} + +void task_exit() { + task_quit = true; + + aptUnhook(&cookie); + + if(task_pause_event != 0) { + svcCloseHandle(task_pause_event); + task_pause_event = 0; + } +} + bool task_is_quit_all() { return task_quit; } -void task_quit_all() { - task_quit = true; +Handle task_get_pause_event() { + return task_pause_event; } \ No newline at end of file diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h index 28380a9..c4346bb 100644 --- a/source/ui/section/task/task.h +++ b/source/ui/section/task/task.h @@ -1,8 +1,7 @@ #pragma once -#include - -#include "../../../core/linkedlist.h" +#define FILE_NAME_MAX 512 +#define FILE_PATH_MAX 512 #define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 1) #define R_FBI_ERRNO MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 2) @@ -11,14 +10,17 @@ #define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY) -typedef struct { +typedef struct linked_list_s linked_list; +typedef struct list_item_s list_item; + +typedef struct meta_info_s { char shortDescription[0x100]; char longDescription[0x200]; char publisher[0x100]; u32 texture; } meta_info; -typedef struct { +typedef struct title_info_s { FS_MediaType mediaType; u64 titleId; char productCode[0x10]; @@ -29,17 +31,17 @@ typedef struct { meta_info meta; } title_info; -typedef struct { +typedef struct pending_title_info_s { FS_MediaType mediaType; u64 titleId; u16 version; } pending_title_info; -typedef struct { +typedef struct ticket_info_s { u64 titleId; } ticket_info; -typedef struct { +typedef struct ext_save_data_info_s { FS_MediaType mediaType; u64 extSaveDataId; bool shared; @@ -47,11 +49,11 @@ typedef struct { meta_info meta; } ext_save_data_info; -typedef struct { +typedef struct system_save_data_info_s { u32 systemSaveDataId; } system_save_data_info; -typedef struct { +typedef struct cia_info_s { u64 titleId; u16 version; u64 installedSize; @@ -59,10 +61,10 @@ typedef struct { meta_info meta; } cia_info; -typedef struct { +typedef struct file_info_s { FS_Archive* archive; - char name[NAME_MAX]; - char path[PATH_MAX]; + char name[FILE_NAME_MAX]; + char path[FILE_PATH_MAX]; bool isDirectory; u64 size; @@ -75,15 +77,15 @@ typedef struct { ticket_info ticketInfo; } file_info; -typedef enum { +typedef enum data_op_e { DATAOP_COPY, DATAOP_DELETE -} DataOp; +} data_op; -typedef struct { +typedef struct data_op_info_s { void* data; - DataOp op; + data_op op; // Copy bool copyEmpty; @@ -118,27 +120,36 @@ typedef struct { bool (*error)(void* data, u32 index, Result res); } data_op_info; +void task_init(); +void task_exit(); bool task_is_quit_all(); -void task_quit_all(); +Handle task_get_pause_event(); Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height); Handle task_data_op(data_op_info* info); +void task_free_ext_save_data(list_item* item); void task_clear_ext_save_data(linked_list* items); Handle task_populate_ext_save_data(linked_list* items); +void task_free_file(list_item* item); void task_clear_files(linked_list* items); +Result task_create_file_item(list_item** out, FS_Archive* archive, const char* path); Handle task_populate_files(linked_list* items, file_info* dir); +void task_free_pending_title(list_item* item); void task_clear_pending_titles(linked_list* items); Handle task_populate_pending_titles(linked_list* items); +void task_free_system_save_data(list_item* item); void task_clear_system_save_data(linked_list* items); Handle task_populate_system_save_data(linked_list* items); +void task_free_ticket(list_item* item); void task_clear_tickets(linked_list* items); Handle task_populate_tickets(linked_list* items); +void task_free_title(list_item* item); void task_clear_titles(linked_list* items); Handle task_populate_titles(linked_list* items); \ No newline at end of file diff --git a/source/ui/section/tickets.c b/source/ui/section/tickets.c index a895827..0bcdc90 100644 --- a/source/ui/section/tickets.c +++ b/source/ui/section/tickets.c @@ -3,11 +3,14 @@ #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" static list_item install_from_cdn = {"Install from CDN", COLOR_TEXT, action_install_cdn}; static list_item delete_ticket = {"Delete Ticket", COLOR_TEXT, action_delete_ticket}; @@ -18,12 +21,12 @@ typedef struct { } tickets_data; typedef struct { - ticket_info* info; - bool* populated; + linked_list* items; + list_item* selected; } tickets_action_data; static void tickets_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_ticket_info(view, ((tickets_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_ticket_info(view, ((tickets_action_data*) data)->selected->data, x1, y1, x2, y2); } static void tickets_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -39,12 +42,12 @@ static void tickets_action_update(ui_view* view, void* data, linked_list* items, } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(ticket_info*, bool*) = (void(*)(ticket_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected); free(data); @@ -57,7 +60,7 @@ static void tickets_action_update(ui_view* view, void* data, linked_list* items, } } -static void tickets_action_open(ticket_info* info, bool* populated) { +static void tickets_action_open(linked_list* items, list_item* selected) { tickets_action_data* data = (tickets_action_data*) calloc(1, sizeof(tickets_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate tickets action data."); @@ -65,8 +68,8 @@ static void tickets_action_open(ticket_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; list_display("Ticket Action", "A: Select, B: Return", data, tickets_action_update, tickets_action_draw_top); } @@ -114,7 +117,7 @@ static void tickets_update(ui_view* view, void* data, linked_list* items, list_i } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - tickets_action_open((ticket_info*) selected->data, &listData->populated); + tickets_action_open(items, selected); return; } } diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index 6c63b91..d179f66 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -3,12 +3,14 @@ #include <3ds.h> -#include "action/action.h" #include "section.h" +#include "action/action.h" +#include "task/task.h" #include "../error.h" #include "../list.h" -#include "../../screen.h" -#include "task/task.h" +#include "../ui.h" +#include "../../core/linkedlist.h" +#include "../../core/screen.h" static list_item launch_title = {"Launch Title", COLOR_TEXT, action_launch_title}; static list_item delete_title = {"Delete Title", COLOR_TEXT, action_delete_title}; @@ -24,12 +26,12 @@ typedef struct { } titles_data; typedef struct { - title_info* info; - bool* populated; + linked_list* items; + list_item* selected; } titles_action_data; static void titles_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { - ui_draw_title_info(view, ((titles_action_data*) data)->info, x1, y1, x2, y2); + ui_draw_title_info(view, ((titles_action_data*) data)->selected->data, x1, y1, x2, y2); } static void titles_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { @@ -45,12 +47,12 @@ static void titles_action_update(ui_view* view, void* data, linked_list* items, } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - void(*action)(title_info*, bool*) = (void(*)(title_info*, bool*)) selected->data; + void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data; ui_pop(); list_destroy(view); - action(actionData->info, actionData->populated); + action(actionData->items, actionData->selected); free(data); @@ -60,15 +62,17 @@ static void titles_action_update(ui_view* view, void* data, linked_list* items, if(linked_list_size(items) == 0) { linked_list_add(items, &launch_title); - if(actionData->info->mediaType != MEDIATYPE_GAME_CARD) { + title_info* info = (title_info*) actionData->selected->data; + + if(info->mediaType != MEDIATYPE_GAME_CARD) { linked_list_add(items, &delete_title); } - if(!actionData->info->twl) { + if(!info->twl) { linked_list_add(items, &extract_smdh); linked_list_add(items, &browse_save_data); - if(actionData->info->mediaType != MEDIATYPE_GAME_CARD) { + if(info->mediaType != MEDIATYPE_GAME_CARD) { linked_list_add(items, &import_secure_value); linked_list_add(items, &export_secure_value); linked_list_add(items, &delete_secure_value); @@ -77,7 +81,7 @@ static void titles_action_update(ui_view* view, void* data, linked_list* items, } } -static void titles_action_open(title_info* info, bool* populated) { +static void titles_action_open(linked_list* items, list_item* selected) { titles_action_data* data = (titles_action_data*) calloc(1, sizeof(titles_action_data)); if(data == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate titles action data."); @@ -85,8 +89,8 @@ static void titles_action_open(title_info* info, bool* populated) { return; } - data->info = info; - data->populated = populated; + data->items = items; + data->selected = selected; list_display("Title Action", "A: Select, B: Return", data, titles_action_update, titles_action_draw_top); } @@ -134,7 +138,7 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it } if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { - titles_action_open((title_info*) selected->data, &listData->populated); + titles_action_open(items, selected); return; } } diff --git a/source/ui/ui.c b/source/ui/ui.c index 4e6853a..7b8c77d 100644 --- a/source/ui/ui.c +++ b/source/ui/ui.c @@ -4,9 +4,9 @@ #include <3ds.h> -#include "section/task/task.h" #include "ui.h" -#include "../screen.h" +#include "section/task/task.h" +#include "../core/screen.h" #define MAX_UI_VIEWS 16