diff --git a/romfs/textcolor.cfg b/romfs/textcolor.cfg index 78cbb17..db7e3de 100644 --- a/romfs/textcolor.cfg +++ b/romfs/textcolor.cfg @@ -4,4 +4,6 @@ sd=FF00FF00 gamecard=FFFF0000 dstitle=FF82004B file=FF000000 -directory=FF0000FF \ No newline at end of file +directory=FF0000FF +enabled=FF00FF00 +disabled=FF0000FF \ No newline at end of file diff --git a/source/core/screen.c b/source/core/screen.c index 2db01ce..7fd7004 100644 --- a/source/core/screen.c +++ b/source/core/screen.c @@ -203,6 +203,10 @@ void screen_init() { colorConfig[COLOR_FILE] = color; } else if(strcasecmp(key, "directory") == 0) { colorConfig[COLOR_DIRECTORY] = color; + } else if(strcasecmp(key, "enabled") == 0) { + colorConfig[COLOR_ENABLED] = color; + } else if(strcasecmp(key, "disabled") == 0) { + colorConfig[COLOR_DISABLED] = color; } } } diff --git a/source/core/screen.h b/source/core/screen.h index 1cdda1c..8acea50 100644 --- a/source/core/screen.h +++ b/source/core/screen.h @@ -41,7 +41,7 @@ #define TEXTURE_WIFI_3 30 #define TEXTURE_AUTO_START 31 -#define NUM_COLORS 7 +#define NUM_COLORS 9 #define COLOR_TEXT 0 #define COLOR_NAND 1 @@ -50,6 +50,8 @@ #define COLOR_DS_TITLE 4 #define COLOR_FILE 5 #define COLOR_DIRECTORY 6 +#define COLOR_ENABLED 7 +#define COLOR_DISABLED 8 void screen_init(); void screen_exit(); diff --git a/source/ui/section/extsavedata.c b/source/ui/section/extsavedata.c index 125d1af..b12ca5a 100644 --- a/source/ui/section/extsavedata.c +++ b/source/ui/section/extsavedata.c @@ -19,6 +19,9 @@ static list_item delete_save_data = {"Delete Save Data", COLOR_TEXT, action_dele typedef struct { populate_ext_save_data_data populateData; + bool showSD; + bool showNAND; + bool populated; } extsavedata_data; @@ -77,6 +80,54 @@ static void extsavedata_action_open(linked_list* items, list_item* selected) { list_display("Ext Save Data Action", "A: Select, B: Return", data, extsavedata_action_update, extsavedata_action_draw_top); } +static void extsavedata_filters_add_entry(linked_list* items, const char* name, bool* val) { + list_item* item = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); + item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + item->data = val; + + linked_list_add(items, item); + } +} + +static void extsavedata_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { + extsavedata_data* listData = (extsavedata_data*) data; + + if(hidKeysDown() & KEY_B) { + linked_list_iter iter; + linked_list_iterate(items, &iter); + + while(linked_list_iter_has_next(&iter)) { + free(linked_list_iter_next(&iter)); + linked_list_iter_remove(&iter); + } + + ui_pop(); + list_destroy(view); + + return; + } + + if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + bool* val = (bool*) selected->data; + *val = !(*val); + + selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + + listData->populated = false; + } + + if(linked_list_size(items) == 0) { + extsavedata_filters_add_entry(items, "Show SD", &listData->showSD); + extsavedata_filters_add_entry(items, "Show NAND", &listData->showNAND); + } +} + +static void extsavedata_filters_open(extsavedata_data* data) { + list_display("Filters", "A: Toggle, B: Return", data, extsavedata_filters_update, NULL); +} + static void extsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { if(selected != NULL && selected->data != NULL) { ui_draw_ext_save_data_info(view, selected->data, x1, y1, x2, y2); @@ -103,6 +154,11 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li return; } + if(hidKeysDown() & KEY_SELECT) { + extsavedata_filters_open(listData); + return; + } + if(!listData->populated || (hidKeysDown() & KEY_X)) { if(!listData->populateData.finished) { svcSignalEvent(listData->populateData.cancelEvent); @@ -132,6 +188,16 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li } } +static bool extsavedata_filter(void* data, u64 titleId, FS_MediaType mediaType) { + extsavedata_data* listData = (extsavedata_data*) data; + + if(mediaType == MEDIATYPE_SD) { + return listData->showSD; + } else { + return listData->showNAND; + } +} + void extsavedata_open() { extsavedata_data* data = (extsavedata_data*) calloc(1, sizeof(extsavedata_data)); if(data == NULL) { @@ -140,7 +206,13 @@ void extsavedata_open() { return; } + data->populateData.filter = extsavedata_filter; + data->populateData.filterData = data; + data->populateData.finished = true; - list_display("Ext Save Data", "A: Select, B: Return, X: Refresh", data, extsavedata_update, extsavedata_draw_top); + data->showSD = true; + data->showNAND = true; + + list_display("Ext Save Data", "A: Select, B: Return, X: Refresh, Select: Filter", data, extsavedata_update, extsavedata_draw_top); } \ No newline at end of file diff --git a/source/ui/section/files.c b/source/ui/section/files.c index e1f3e46..a460020 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -48,6 +48,11 @@ typedef struct { FS_Path archivePath; FS_Archive archive; + bool showDirectories; + bool showCias; + bool showTickets; + bool showMisc; + char currDir[FILE_PATH_MAX]; list_item* dirItem; } files_data; @@ -152,6 +157,56 @@ static void files_action_open(linked_list* items, list_item* selected, files_dat list_display(((file_info*) selected->data)->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", data, files_action_update, files_action_draw_top); } +static void files_filters_add_entry(linked_list* items, const char* name, bool* val) { + list_item* item = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); + item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + item->data = val; + + linked_list_add(items, item); + } +} + +static void files_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { + files_data* listData = (files_data*) data; + + if(hidKeysDown() & KEY_B) { + linked_list_iter iter; + linked_list_iterate(items, &iter); + + while(linked_list_iter_has_next(&iter)) { + free(linked_list_iter_next(&iter)); + linked_list_iter_remove(&iter); + } + + ui_pop(); + list_destroy(view); + + return; + } + + if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + bool* val = (bool*) selected->data; + *val = !(*val); + + selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + + listData->populated = false; + } + + if(linked_list_size(items) == 0) { + files_filters_add_entry(items, "Show directories", &listData->showDirectories); + files_filters_add_entry(items, "Show CIAs", &listData->showCias); + files_filters_add_entry(items, "Show tickets", &listData->showTickets); + files_filters_add_entry(items, "Show miscellaneous", &listData->showMisc); + } +} + +static void files_filters_open(files_data* data) { + list_display("Filters", "A: Toggle, B: Return", data, files_filters_update, NULL); +} + static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { if(selected != NULL && selected->data != NULL) { ui_draw_file_info(view, selected->data, x1, y1, x2, y2); @@ -246,6 +301,11 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite } } + if(hidKeysDown() & KEY_SELECT) { + files_filters_open(listData); + return; + } + if((hidKeysDown() & KEY_Y) && listData->dirItem != NULL) { files_action_open(items, listData->dirItem, listData); return; @@ -273,6 +333,26 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite } } +static bool files_filter(void* data, const char* name, u32 attributes) { + files_data* listData = (files_data*) data; + + if((attributes & FS_ATTRIBUTE_DIRECTORY) != 0) { + return listData->showDirectories; + } else { + size_t len = strlen(name); + if(len >= 4) { + const char* extension = name + len - 4; + if(strncmp(extension, ".cia", 4) == 0) { + return listData->showCias; + } else if(strncmp(extension, ".tik", 4) == 0) { + return listData->showTickets; + } + } + } + + return listData->showMisc; +} + void files_open(FS_ArchiveID archiveId, FS_Path archivePath) { files_data* data = (files_data*) calloc(1, sizeof(files_data)); if(data == NULL) { @@ -285,10 +365,18 @@ void files_open(FS_ArchiveID archiveId, FS_Path archivePath) { data->populateData.includeBase = false; data->populateData.dirsFirst = true; + data->populateData.filter = files_filter; + data->populateData.filterData = data; + data->populateData.finished = true; data->populated = false; + data->showDirectories = true; + data->showCias = true; + data->showTickets = true; + data->showMisc = true; + data->archiveId = archiveId; data->archivePath.type = archivePath.type; data->archivePath.size = archivePath.size; @@ -317,7 +405,7 @@ void files_open(FS_ArchiveID archiveId, FS_Path archivePath) { return; } - list_display("Files", "A: Select, B: Back, X: Refresh, Y: Directory Action", data, files_update, files_draw_top); + list_display("Files", "A: Select, B: Back, X: Refresh, Y: Dir, Select: Filter", data, files_update, files_draw_top); } void files_open_sd() { diff --git a/source/ui/section/task/listextsavedata.c b/source/ui/section/task/listextsavedata.c index cf59d2d..32d5a4a 100644 --- a/source/ui/section/task/listextsavedata.c +++ b/source/ui/section/task/listextsavedata.c @@ -35,73 +35,75 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data 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; + if(data->filter == NULL || data->filter(data->filterData, extSaveDataIds[i], mediaType)) { + 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]}; + FS_ExtSaveDataInfo info = {.mediaType = mediaType, .saveId = extSaveDataIds[i]}; - SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); - if(smdh != NULL) { - u32 smdhBytesRead = 0; - if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) smdh)) && smdhBytesRead == sizeof(SMDH)) { - if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') { - u8 systemLanguage = CFG_LANGUAGE_EN; - CFGU_GetSystemLanguage(&systemLanguage); + SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH)); + if(smdh != NULL) { + u32 smdhBytesRead = 0; + if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) smdh)) && smdhBytesRead == sizeof(SMDH)) { + if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') { + u8 systemLanguage = CFG_LANGUAGE_EN; + CFGU_GetSystemLanguage(&systemLanguage); - utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, LIST_ITEM_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); + 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); + } + } + + free(smdh); + } + + bool empty = strlen(item->name) == 0; + if(!empty) { + empty = true; + + char* curr = item->name; + while(*curr) { + if(*curr != ' ') { + empty = false; + break; + } + + curr++; } } - free(smdh); - } - - 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; } - - 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; } } } diff --git a/source/ui/section/task/listfiles.c b/source/ui/section/task/listfiles.c index e81288c..3adf577 100644 --- a/source/ui/section/task/listfiles.c +++ b/source/ui/section/task/listfiles.c @@ -4,6 +4,7 @@ #include #include <3ds.h> +#include <3ds/services/fs.h> #include "task.h" #include "../../list.h" @@ -192,25 +193,27 @@ static void task_populate_files_thread(void* arg) { char name[FILE_NAME_MAX] = {'\0'}; utf16_to_utf8((uint8_t*) name, entries[i].name, FILE_NAME_MAX - 1); - char path[FILE_PATH_MAX] = {'\0'}; - snprintf(path, FILE_PATH_MAX, "%s%s", curr->path, name); + if(data->filter == NULL || data->filter(data->filterData, name, entries[i].attributes)) { + char path[FILE_PATH_MAX] = {'\0'}; + snprintf(path, FILE_PATH_MAX, "%s%s", curr->path, name); - list_item* item = NULL; - if(R_SUCCEEDED(res = task_create_file_item(&item, curr->archive, path))) { - if(curr->isDirectory && strncmp(curr->path, data->base->path, FILE_PATH_MAX) == 0) { - file_info* info = (file_info*) item->data; + list_item* item = NULL; + if(R_SUCCEEDED(res = task_create_file_item(&item, curr->archive, path))) { + if(curr->isDirectory && strncmp(curr->path, data->base->path, FILE_PATH_MAX) == 0) { + file_info* info = (file_info*) item->data; - if(info->isCia) { - data->base->containsCias = true; - } else if(info->isTicket) { - data->base->containsTickets = true; + if(info->isCia) { + data->base->containsCias = true; + } else if(info->isTicket) { + data->base->containsTickets = true; + } } - } - if(data->recursive && ((file_info*) item->data)->isDirectory) { - linked_list_add(&queue, item); - } else { - linked_list_add(data->items, item); + if(data->recursive && ((file_info*) item->data)->isDirectory) { + linked_list_add(&queue, item); + } else { + linked_list_add(data->items, item); + } } } } diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c index 09258cd..6a3fd61 100644 --- a/source/ui/section/task/listtitles.c +++ b/source/ui/section/task/listtitles.c @@ -273,12 +273,14 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType break; } - bool dsiWare = ((titleIds[i] >> 32) & 0x8000) != 0; - if(dsiWare != useDSiWare) { - continue; - } + if(data->filter == NULL || data->filter(data->filterData, titleIds[i], mediaType)) { + bool dsiWare = ((titleIds[i] >> 32) & 0x8000) != 0; + if(dsiWare != useDSiWare) { + continue; + } - res = dsiWare ? task_populate_titles_add_twl(data, mediaType, titleIds[i]) : task_populate_titles_add_ctr(data, mediaType, titleIds[i]); + res = dsiWare ? task_populate_titles_add_twl(data, mediaType, titleIds[i]) : task_populate_titles_add_ctr(data, mediaType, titleIds[i]); + } } } diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h index 12bb5c7..d6abdd7 100644 --- a/source/ui/section/task/task.h +++ b/source/ui/section/task/task.h @@ -130,6 +130,9 @@ typedef struct data_op_info_s { typedef struct { linked_list* items; + bool (*filter)(void* data, u64 extSaveDataId, FS_MediaType mediaType); + void* filterData; + volatile bool finished; Result result; Handle cancelEvent; @@ -144,6 +147,9 @@ typedef struct { bool includeBase; bool dirsFirst; + bool (*filter)(void* data, const char* name, u32 attributes); + void* filterData; + volatile bool finished; Result result; Handle cancelEvent; @@ -176,6 +182,9 @@ typedef struct { typedef struct { linked_list* items; + bool (*filter)(void* data, u64 titleId, FS_MediaType mediaType); + void* filterData; + volatile bool finished; Result result; Handle cancelEvent; diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index 2f6c7f2..b5893e7 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -24,6 +24,10 @@ static list_item delete_secure_value = {"Delete Secure Value", COLOR_TEXT, actio typedef struct { populate_titles_data populateData; + bool showGameCard; + bool showSD; + bool showNAND; + bool populated; } titles_data; @@ -102,6 +106,55 @@ static void titles_action_open(linked_list* items, list_item* selected) { list_display("Title Action", "A: Select, B: Return", data, titles_action_update, titles_action_draw_top); } +static void titles_filters_add_entry(linked_list* items, const char* name, bool* val) { + list_item* item = (list_item*) calloc(1, sizeof(list_item)); + if(item != NULL) { + snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name); + item->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + item->data = val; + + linked_list_add(items, item); + } +} + +static void titles_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { + titles_data* listData = (titles_data*) data; + + if(hidKeysDown() & KEY_B) { + linked_list_iter iter; + linked_list_iterate(items, &iter); + + while(linked_list_iter_has_next(&iter)) { + free(linked_list_iter_next(&iter)); + linked_list_iter_remove(&iter); + } + + ui_pop(); + list_destroy(view); + + return; + } + + if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) { + bool* val = (bool*) selected->data; + *val = !(*val); + + selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED; + + listData->populated = false; + } + + if(linked_list_size(items) == 0) { + titles_filters_add_entry(items, "Show game card", &listData->showGameCard); + titles_filters_add_entry(items, "Show SD", &listData->showSD); + titles_filters_add_entry(items, "Show NAND", &listData->showNAND); + } +} + +static void titles_filters_open(titles_data* data) { + list_display("Filters", "A: Toggle, B: Return", data, titles_filters_update, NULL); +} + static void titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { if(selected != NULL && selected->data != NULL) { ui_draw_title_info(view, selected->data, x1, y1, x2, y2); @@ -128,6 +181,11 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it return; } + if(hidKeysDown() & KEY_SELECT) { + titles_filters_open(listData); + return; + } + if(!listData->populated || (hidKeysDown() & KEY_X)) { if(!listData->populateData.finished) { svcSignalEvent(listData->populateData.cancelEvent); @@ -157,6 +215,18 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it } } +static bool titles_filter(void* data, u64 titleId, FS_MediaType mediaType) { + titles_data* listData = (titles_data*) data; + + if(mediaType == MEDIATYPE_GAME_CARD) { + return listData->showGameCard; + } else if(mediaType == MEDIATYPE_SD) { + return listData->showSD; + } else { + return listData->showNAND; + } +} + void titles_open() { titles_data* data = (titles_data*) calloc(1, sizeof(titles_data)); if(data == NULL) { @@ -165,7 +235,14 @@ void titles_open() { return; } + data->populateData.filter = titles_filter; + data->populateData.filterData = data; + data->populateData.finished = true; - list_display("Titles", "A: Select, B: Return, X: Refresh", data, titles_update, titles_draw_top); + data->showGameCard = true; + data->showSD = true; + data->showNAND = true; + + list_display("Titles", "A: Select, B: Return, X: Refresh, Select: Filter", data, titles_update, titles_draw_top); } \ No newline at end of file