From 2b57845edd2aed6cb4edbd618f5fb4991aa4705f Mon Sep 17 00:00:00 2001 From: Steveice10 Date: Sat, 17 Dec 2016 12:03:06 -0800 Subject: [PATCH] Allow sorting titles and ext save data by name. --- source/core/linkedlist.c | 57 +++++++----- source/core/linkedlist.h | 3 +- source/core/util.c | 23 ++++- source/core/util.h | 4 +- source/ui/list.c | 106 ++++++++++++++--------- source/ui/section/action/newfolder.c | 2 +- source/ui/section/action/pastecontents.c | 2 +- source/ui/section/action/rename.c | 2 +- source/ui/section/extsavedata.c | 58 +++++++++++-- source/ui/section/files.c | 22 ++--- source/ui/section/task/listextsavedata.c | 23 ++--- source/ui/section/task/listtitledb.c | 8 +- source/ui/section/task/listtitles.c | 40 ++------- source/ui/section/task/task.h | 22 ++--- source/ui/section/titles.c | 66 +++++++++++--- 15 files changed, 273 insertions(+), 165 deletions(-) diff --git a/source/core/linkedlist.c b/source/core/linkedlist.c index 99d0f1a..9ac791b 100644 --- a/source/core/linkedlist.c +++ b/source/core/linkedlist.c @@ -31,16 +31,22 @@ void linked_list_clear(linked_list* list) { } bool linked_list_contains(linked_list* list, void* value) { + return linked_list_index_of(list, value) != -1; +} + +int linked_list_index_of(linked_list* list, void* value) { + int i = 0; linked_list_node* node = list->first; while(node != NULL) { if(node->value == value) { - return true; + return i; } + i++; node = node->next; } - return false; + return -1; } static linked_list_node* linked_list_get_node(linked_list* list, unsigned int index) { @@ -186,27 +192,28 @@ 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 linked_list_sort(linked_list* list, void* userData, int (*compare)(void* userData, const void* p1, const void* p2)) { + bool swapped = true; + while(swapped) { + swapped = false; - void** elements = (void**) calloc(count, sizeof(void*)); - if(elements != NULL) { - unsigned int num = 0; - linked_list_node* node = list->first; - while(node != NULL && num < count) { - elements[num++] = node->value; - node = node->next; + linked_list_node* curr = list->first; + if(curr == NULL) { + return; } - linked_list_clear(list); + linked_list_node* next = NULL; + while((next = curr->next) != NULL) { + if(compare(userData, curr->value, next->value) > 0) { + void* temp = curr->value; + curr->value = next->value; + next->value = temp; - qsort(elements, num, sizeof(void*), (int (*)(const void* p1, const void* p2)) compare); + swapped = true; + } - for(unsigned int i = 0; i < num; i++) { - linked_list_add(list, elements[i]); + curr = next; } - - free(elements); } } @@ -225,17 +232,23 @@ void linked_list_iter_restart(linked_list_iter* iter) { } bool linked_list_iter_has_next(linked_list_iter* iter) { - return iter->next != NULL; + return iter->next != NULL && iter->next->value != NULL; } void* linked_list_iter_next(linked_list_iter* iter) { - if(iter->next == NULL) { + linked_list_node* next = iter->next; + if(next == NULL) { return NULL; } - iter->curr = iter->next; - iter->next = iter->next->next; - return iter->curr->value; + void* value = next->value; + if(value == NULL) { + return NULL; + } + + iter->curr = next; + iter->next = next->next; + return value; } void linked_list_iter_remove(linked_list_iter* iter) { diff --git a/source/core/linkedlist.h b/source/core/linkedlist.h index 930b925..73a69ac 100644 --- a/source/core/linkedlist.h +++ b/source/core/linkedlist.h @@ -26,12 +26,13 @@ void linked_list_destroy(linked_list* list); unsigned int linked_list_size(linked_list* list); void linked_list_clear(linked_list* list); bool linked_list_contains(linked_list* list, void* value); +int linked_list_index_of(linked_list* list, void* value); void* linked_list_get(linked_list* list, unsigned int index); 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_sort(linked_list* list, void* userData, int (*compare)(void* userData, const void* p1, const void* p2)); void linked_list_iterate(linked_list* list, linked_list_iter* iter); diff --git a/source/core/util.c b/source/core/util.c index c52f181..d037868 100644 --- a/source/core/util.c +++ b/source/core/util.c @@ -255,6 +255,23 @@ void util_get_parent_path(char* out, const char* path, u32 size) { out[terminatorPos] = '\0'; } +bool util_is_string_empty(const char* str) { + if(strlen(str) == 0) { + return true; + } + + const char* curr = str; + while(*curr) { + if(*curr != ' ') { + return false; + } + + curr++; + } + + return true; +} + static Result FSUSER_AddSeed(u64 titleId, const void* seed) { u32 *cmdbuf = getThreadCommandBuffer(); @@ -417,9 +434,9 @@ bool util_filter_tickets(void* data, const char* name, u32 attributes) { return len >= 4 && strncasecmp(name + len - 4, ".tik", 4) == 0; } -int util_compare_file_infos(const void** p1, const void** p2) { - list_item* info1 = *(list_item**) p1; - list_item* info2 = *(list_item**) p2; +int util_compare_file_infos(void* userData, const void* p1, const void* p2) { + list_item* info1 = (list_item*) p1; + list_item* info2 = (list_item*) p2; bool info1Base = strncmp(info1->name, "", LIST_ITEM_NAME_MAX) == 0 || strncmp(info1->name, "", LIST_ITEM_NAME_MAX) == 0; bool info2Base = strncmp(info2->name, "", LIST_ITEM_NAME_MAX) == 0 || strncmp(info2->name, "", LIST_ITEM_NAME_MAX) == 0; diff --git a/source/core/util.h b/source/core/util.h index 530466f..373f115 100644 --- a/source/core/util.h +++ b/source/core/util.h @@ -52,6 +52,8 @@ 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_is_string_empty(const char* str); + Result util_import_seed(u64 titleId); u32 util_get_seed_response_code(); @@ -67,7 +69,7 @@ u8* util_get_tmd_content_chunk(u8* tmd, u32 index); bool util_filter_cias(void* data, const char* name, u32 attributes); bool util_filter_tickets(void* data, const char* name, u32 attributes); -int util_compare_file_infos(const void** p1, const void** p2); +int util_compare_file_infos(void* userData, const void* p1, const void* p2); const char* util_get_3dsx_path(); void util_set_3dsx_path(const char* path); diff --git a/source/ui/list.c b/source/ui/list.c index 03407ad..975b0c2 100644 --- a/source/ui/list.c +++ b/source/ui/list.c @@ -12,6 +12,7 @@ typedef struct { void* data; linked_list items; u32 selectedIndex; + list_item* selectedItem; u32 selectionScroll; u64 nextSelectionScrollResetTime; float scrollPos; @@ -21,27 +22,31 @@ typedef struct { void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected); } list_data; -static float list_get_item_screen_y(list_data* listData, u32 index) { +static float list_get_item_screen_y(list_data* listData, list_item* item) { + if(item == NULL) { + return 0; + } + float y = -listData->scrollPos; linked_list_iter iter; linked_list_iterate(&listData->items, &iter); - int i = 0; - while(linked_list_iter_has_next(&iter) && i < index) { - list_item* item = (list_item*) linked_list_iter_next(&iter); + while(linked_list_iter_has_next(&iter)) { + list_item* currItem = (list_item*) linked_list_iter_next(&iter); + if(currItem == item) { + break; + } float stringHeight; - screen_get_string_size(NULL, &stringHeight, item->name, 0.5f, 0.5f); + screen_get_string_size(NULL, &stringHeight, currItem->name, 0.5f, 0.5f); y += stringHeight; - - i++; } return y; } -static int list_get_item_at(list_data* listData, float screenY) { +static int list_get_item_index_at(list_data* listData, float screenY) { float y = -listData->scrollPos; linked_list_iter iter; @@ -71,19 +76,48 @@ static void list_validate_pos(list_data* listData, float by1, float by2) { if(size == 0 || listData->selectedIndex < 0) { listData->selectedIndex = 0; + listData->selectedItem = NULL; listData->scrollPos = 0; } if(size > 0) { if(listData->selectedIndex > size - 1) { listData->selectedIndex = size - 1; + listData->selectedItem = NULL; listData->scrollPos = 0; } - float lastItemHeight; - screen_get_string_size(NULL, &lastItemHeight, ((list_item*) linked_list_get(&listData->items, size - 1))->name, 0.5f, 0.5f); + bool found = false; + if(listData->selectedItem != NULL) { + int index = linked_list_index_of(&listData->items, listData->selectedItem); + if(index != -1) { + found = true; + listData->selectedIndex = (u32) index; + } + } - float lastPageEnd = list_get_item_screen_y(listData, size - 1); + if(!found) { + listData->selectedItem = linked_list_get(&listData->items, listData->selectedIndex); + } + + float itemHeight; + screen_get_string_size(NULL, &itemHeight, listData->selectedItem->name, 0.5f, 0.5f); + + float itemY = list_get_item_screen_y(listData, listData->selectedItem); + if(itemY + itemHeight > by2 - by1) { + listData->scrollPos -= (by2 - by1) - itemY - itemHeight; + } + + if(itemY < 0) { + listData->scrollPos += itemY; + } + + list_item* lastItem = (list_item*) linked_list_get(&listData->items, size - 1); + + float lastItemHeight; + screen_get_string_size(NULL, &lastItemHeight, lastItem->name, 0.5f, 0.5f); + + float lastPageEnd = list_get_item_screen_y(listData, lastItem); if(lastPageEnd < by2 - by1 - lastItemHeight) { listData->scrollPos -= (by2 - by1 - lastItemHeight) - lastPageEnd; } @@ -104,7 +138,7 @@ static void list_update(ui_view* view, void* data, float bx1, float by1, float b list_validate_pos(listData, by1, by2); float itemWidth; - screen_get_string_size(&itemWidth, NULL, ((list_item*) linked_list_get(&listData->items, listData->selectedIndex))->name, 0.5f, 0.5f); + screen_get_string_size(&itemWidth, NULL, listData->selectedItem->name, 0.5f, 0.5f); if(itemWidth > bx2 - bx1) { if(listData->selectionScroll == 0 || listData->selectionScroll >= itemWidth - (bx2 - bx1)) { if(listData->nextSelectionScrollResetTime == 0) { @@ -165,30 +199,13 @@ static void list_update(ui_view* view, void* data, float bx1, float by1, float b listData->nextActionTime = osGetTime() + ((hidKeysDown() & KEY_LEFT) ? 500 : 100); } - if(lastSelectedIndex != listData->selectedIndex) { - listData->selectionScroll = 0; - listData->nextSelectionScrollResetTime = 0; - - float itemHeight; - screen_get_string_size(NULL, &itemHeight, ((list_item*) linked_list_get(&listData->items, listData->selectedIndex))->name, 0.5f, 0.5f); - - float itemY = list_get_item_screen_y(listData, listData->selectedIndex); - if(itemY + itemHeight > by2 - by1) { - listData->scrollPos -= (by2 - by1) - itemY - itemHeight; - } - - if(itemY < 0) { - listData->scrollPos += itemY; - } - } - if(hidKeysDown() & KEY_TOUCH) { touchPosition pos; hidTouchRead(&pos); listData->lastScrollTouchY = pos.py; - int index = list_get_item_at(listData, pos.py - by1); + int index = list_get_item_index_at(listData, pos.py - by1); if(index >= 0) { if(listData->selectedIndex == index) { selectedTouched = true; @@ -204,11 +221,20 @@ static void list_update(ui_view* view, void* data, float bx1, float by1, float b listData->lastScrollTouchY = pos.py; } + if(listData->selectedIndex != lastSelectedIndex) { + listData->selectedItem = linked_list_get(&listData->items, listData->selectedIndex); + + listData->selectionScroll = 0; + listData->nextSelectionScrollResetTime = 0; + } + list_validate_pos(listData, by1, by2); } if(listData->update != NULL) { - listData->update(view, listData->data, &listData->items, linked_list_get(&listData->items, listData->selectedIndex), selectedTouched); + listData->update(view, listData->data, &listData->items, listData->selectedItem, selectedTouched); + + list_validate_pos(listData, by1, by2); } } @@ -216,7 +242,7 @@ static void list_draw_top(ui_view* view, void* data, float x1, float y1, float x list_data* listData = (list_data*) data; if(listData->drawTop != NULL) { - listData->drawTop(view, listData->data, x1, y1, x2, y2, linked_list_get(&listData->items, listData->selectedIndex)); + listData->drawTop(view, listData->data, x1, y1, x2, y2, listData->selectedItem); } } @@ -230,7 +256,6 @@ static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, floa linked_list_iter iter; linked_list_iterate(&listData->items, &iter); - u32 i = 0; while(linked_list_iter_has_next(&iter)) { if(y > y2) { break; @@ -243,13 +268,13 @@ static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, floa if(y > y1 - stringHeight) { float x = x1 + 2; - if(i == listData->selectedIndex) { + if(item == listData->selectedItem) { x -= listData->selectionScroll; } screen_draw_string(item->name, x, y, 0.5f, 0.5f, item->color, true); - if(i == listData->selectedIndex) { + if(item == listData->selectedItem) { u32 selectionOverlayWidth = 0; u32 selectionOverlayHeight = 0; screen_get_texture_size(&selectionOverlayWidth, &selectionOverlayHeight, TEXTURE_SELECTION_OVERLAY); @@ -258,16 +283,16 @@ static void list_draw_bottom(ui_view* view, void* data, float x1, float y1, floa } y += stringHeight; - - i++; } u32 size = linked_list_size(&listData->items); if(size > 0) { - float lastItemHeight; - screen_get_string_size(NULL, &lastItemHeight, ((list_item*) linked_list_get(&listData->items, size - 1))->name, 0.5f, 0.5f); + list_item* lastItem = (list_item*) linked_list_get(&listData->items, size - 1); - float totalHeight = list_get_item_screen_y(listData, size - 1) + listData->scrollPos + lastItemHeight; + float lastItemHeight; + screen_get_string_size(NULL, &lastItemHeight, lastItem->name, 0.5f, 0.5f); + + float totalHeight = list_get_item_screen_y(listData, lastItem) + listData->scrollPos + lastItemHeight; float viewHeight = y2 - y1; @@ -297,6 +322,7 @@ ui_view* list_display(const char* name, const char* info, void* data, void (*upd listData->data = data; linked_list_init(&listData->items); listData->selectedIndex = 0; + listData->selectedItem = NULL; listData->selectionScroll = 0; listData->nextSelectionScrollResetTime = 0; listData->scrollPos = 0; diff --git a/source/ui/section/action/newfolder.c b/source/ui/section/action/newfolder.c index b06f321..96b9a5c 100644 --- a/source/ui/section/action/newfolder.c +++ b/source/ui/section/action/newfolder.c @@ -43,7 +43,7 @@ void action_new_folder(linked_list* items, list_item* selected) { list_item* folderItem = NULL; if(R_SUCCEEDED(task_create_file_item(&folderItem, parentDir->archive, path, FS_ATTRIBUTE_DIRECTORY))) { linked_list_add(items, folderItem); - linked_list_sort(items, util_compare_file_infos); + linked_list_sort(items, NULL, util_compare_file_infos); } prompt_display("Success", "Folder created.", COLOR_TEXT, false, NULL, NULL, NULL); diff --git a/source/ui/section/action/pastecontents.c b/source/ui/section/action/pastecontents.c index 783af94..150316d 100644 --- a/source/ui/section/action/pastecontents.c +++ b/source/ui/section/action/pastecontents.c @@ -255,7 +255,7 @@ static void action_paste_contents_update(ui_view* view, void* data, float* progr if(pasteData->pasteInfo.finished) { FSUSER_ControlArchive(pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - linked_list_sort(pasteData->items, util_compare_file_infos); + linked_list_sort(pasteData->items, NULL, util_compare_file_infos); ui_pop(); info_destroy(view); diff --git a/source/ui/section/action/rename.c b/source/ui/section/action/rename.c index 767a567..c6209a5 100644 --- a/source/ui/section/action/rename.c +++ b/source/ui/section/action/rename.c @@ -66,7 +66,7 @@ void action_rename(linked_list* items, list_item* selected) { strncpy(targetInfo->name, textBuf, FILE_NAME_MAX); strncpy(targetInfo->path, dstPath, FILE_PATH_MAX); - linked_list_sort(items, util_compare_file_infos); + linked_list_sort(items, NULL, util_compare_file_infos); prompt_display("Success", "Renamed.", COLOR_TEXT, false, NULL, NULL, NULL); } else { diff --git a/source/ui/section/extsavedata.c b/source/ui/section/extsavedata.c index f8397e0..104e00a 100644 --- a/source/ui/section/extsavedata.c +++ b/source/ui/section/extsavedata.c @@ -1,5 +1,6 @@ #include #include +#include #include <3ds.h> @@ -11,6 +12,7 @@ #include "../ui.h" #include "../../core/linkedlist.h" #include "../../core/screen.h" +#include "../../core/util.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}; @@ -21,6 +23,7 @@ typedef struct { bool showSD; bool showNAND; + bool sortByName; bool populated; } extsavedata_data; @@ -80,7 +83,7 @@ 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) { +static void extsavedata_options_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); @@ -91,7 +94,7 @@ static void extsavedata_filters_add_entry(linked_list* items, const char* name, } } -static void extsavedata_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { +static void extsavedata_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { extsavedata_data* listData = (extsavedata_data*) data; if(hidKeysDown() & KEY_B) { @@ -119,13 +122,14 @@ static void extsavedata_filters_update(ui_view* view, void* data, linked_list* i } if(linked_list_size(items) == 0) { - extsavedata_filters_add_entry(items, "Show SD", &listData->showSD); - extsavedata_filters_add_entry(items, "Show NAND", &listData->showNAND); + extsavedata_options_add_entry(items, "Show SD", &listData->showSD); + extsavedata_options_add_entry(items, "Show NAND", &listData->showNAND); + extsavedata_options_add_entry(items, "Sort by name", &listData->sortByName); } } -static void extsavedata_filters_open(extsavedata_data* data) { - list_display("Filters", "A: Toggle, B: Return", data, extsavedata_filters_update, NULL); +static void extsavedata_options_open(extsavedata_data* data) { + list_display("Options", "A: Toggle, B: Return", data, extsavedata_options_update, NULL); } static void extsavedata_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { @@ -155,7 +159,7 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li } if(hidKeysDown() & KEY_SELECT) { - extsavedata_filters_open(listData); + extsavedata_options_open(listData); return; } @@ -198,6 +202,40 @@ static bool extsavedata_filter(void* data, u64 titleId, FS_MediaType mediaType) } } +static int extsavedata_compare(void* data, const void* p1, const void* p2) { + extsavedata_data* listData = (extsavedata_data*) data; + + list_item* info1 = (list_item*) p1; + list_item* info2 = (list_item*) p2; + + ext_save_data_info* title1 = (ext_save_data_info*) info1->data; + ext_save_data_info* title2 = (ext_save_data_info*) info2->data; + + if(title1->mediaType > title2->mediaType) { + return -1; + } else if(title1->mediaType < title2->mediaType) { + return 1; + } else { + if(listData->sortByName) { + bool title1HasName = title1->hasMeta && !util_is_string_empty(title1->meta.shortDescription); + bool title2HasName = title2->hasMeta && !util_is_string_empty(title2->meta.shortDescription); + + if(title1HasName && !title2HasName) { + return -1; + } else if(!title1HasName && title2HasName) { + return 1; + } else { + return strcasecmp(info1->name, info2->name); + } + } else { + u64 id1 = title1->extSaveDataId; + u64 id2 = title2->extSaveDataId; + + return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; + } + } +} + void extsavedata_open() { extsavedata_data* data = (extsavedata_data*) calloc(1, sizeof(extsavedata_data)); if(data == NULL) { @@ -206,13 +244,15 @@ void extsavedata_open() { return; } + data->populateData.userData = data; data->populateData.filter = extsavedata_filter; - data->populateData.filterData = data; + data->populateData.compare = extsavedata_compare; data->populateData.finished = true; data->showSD = true; data->showNAND = true; + data->sortByName = true; - list_display("Ext Save Data", "A: Select, B: Return, X: Refresh, Select: Filter", data, extsavedata_update, extsavedata_draw_top); + list_display("Ext Save Data", "A: Select, B: Return, X: Refresh, Select: Options", 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 4c423b8..6aa29b4 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -181,7 +181,7 @@ static void files_action_open(linked_list* items, list_item* selected, files_dat list_display((((file_info*) selected->data)->attributes & FS_ATTRIBUTE_DIRECTORY) ? "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) { +static void files_options_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); @@ -192,7 +192,7 @@ static void files_filters_add_entry(linked_list* items, const char* name, bool* } } -static void files_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { +static void files_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { files_data* listData = (files_data*) data; if(hidKeysDown() & KEY_B) { @@ -220,16 +220,16 @@ static void files_filters_update(ui_view* view, void* data, linked_list* items, } if(linked_list_size(items) == 0) { - files_filters_add_entry(items, "Show hidden", &listData->showHidden); - files_filters_add_entry(items, "Show directories", &listData->showDirectories); - files_filters_add_entry(items, "Show files", &listData->showFiles); - files_filters_add_entry(items, "Show CIAs", &listData->showCias); - files_filters_add_entry(items, "Show tickets", &listData->showTickets); + files_options_add_entry(items, "Show hidden", &listData->showHidden); + files_options_add_entry(items, "Show directories", &listData->showDirectories); + files_options_add_entry(items, "Show files", &listData->showFiles); + files_options_add_entry(items, "Show CIAs", &listData->showCias); + files_options_add_entry(items, "Show tickets", &listData->showTickets); } } -static void files_filters_open(files_data* data) { - list_display("Filters", "A: Toggle, B: Return", data, files_filters_update, NULL); +static void files_options_open(files_data* data) { + list_display("Options", "A: Toggle, B: Return", data, files_options_update, NULL); } static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { @@ -322,7 +322,7 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite } if(hidKeysDown() & KEY_SELECT) { - files_filters_open(listData); + files_options_open(listData); return; } @@ -421,7 +421,7 @@ void files_open(FS_ArchiveID archiveId, FS_Path archivePath) { return; } - list_display("Files", "A: Select, B: Back, X: Refresh, Select: Filters", data, files_update, files_draw_top); + list_display("Files", "A: Select, B: Back, X: Refresh, Select: Options", 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 6b21dc6..5120d8d 100644 --- a/source/ui/section/task/listextsavedata.c +++ b/source/ui/section/task/listextsavedata.c @@ -35,7 +35,7 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data break; } - if(data->filter == NULL || data->filter(data->filterData, extSaveDataIds[i], mediaType)) { + if(data->filter == NULL || data->filter(data->userData, 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)); @@ -69,22 +69,7 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data 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) { + if(util_is_string_empty(item->name)) { snprintf(item->name, LIST_ITEM_NAME_MAX, "%016llX", extSaveDataIds[i]); } @@ -97,6 +82,10 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data item->data = extSaveDataInfo; linked_list_add(data->items, item); + + if(data->compare != NULL) { + linked_list_sort(data->items, data->userData, data->compare); + } } else { free(item); diff --git a/source/ui/section/task/listtitledb.c b/source/ui/section/task/listtitledb.c index a6aa3e8..d88ebe1 100644 --- a/source/ui/section/task/listtitledb.c +++ b/source/ui/section/task/listtitledb.c @@ -55,9 +55,9 @@ static Result task_populate_titledb_download(u32* downloadSize, void* buffer, u3 return res; } -static int task_populate_titledb_compare(const void** p1, const void** p2) { - list_item* info1 = *(list_item**) p1; - list_item* info2 = *(list_item**) p2; +static int task_populate_titledb_compare(void* userData, const void* p1, const void* p2) { + list_item* info1 = (list_item*) p1; + list_item* info2 = (list_item*) p2; return strncasecmp(info1->name, info2->name, LIST_ITEM_NAME_MAX); } @@ -151,7 +151,7 @@ static void task_populate_titledb_thread(void* arg) { } if(R_SUCCEEDED(res)) { - linked_list_sort(&tempItems, task_populate_titledb_compare); + linked_list_sort(&tempItems, NULL, task_populate_titledb_compare); linked_list_iter tempIter; linked_list_iterate(&tempItems, &tempIter); diff --git a/source/ui/section/task/listtitles.c b/source/ui/section/task/listtitles.c index 4908bfc..413c9b0 100644 --- a/source/ui/section/task/listtitles.c +++ b/source/ui/section/task/listtitles.c @@ -61,22 +61,7 @@ static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaT FSFILE_Close(fileHandle); } - bool empty = strlen(item->name) == 0; - if(!empty) { - empty = true; - - char* curr = item->name; - while(*curr) { - if(*curr != ' ') { - empty = false; - break; - } - - curr++; - } - } - - if(empty) { + if(util_is_string_empty(item->name)) { snprintf(item->name, NAME_MAX, "%016llX", titleId); } @@ -214,22 +199,7 @@ static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaT free(bnr); } - bool empty = strlen(item->name) == 0; - if(!empty) { - empty = true; - - char* curr = item->name; - while(*curr) { - if(*curr != ' ') { - empty = false; - break; - } - - curr++; - } - } - - if(empty) { + if(util_is_string_empty(item->name)) { snprintf(item->name, NAME_MAX, "%016llX", realTitleId); } @@ -280,13 +250,15 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType break; } - if(data->filter == NULL || data->filter(data->filterData, titleIds[i], mediaType)) { + if(data->filter == NULL || data->filter(data->userData, 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]); + if(R_SUCCEEDED(res = dsiWare ? task_populate_titles_add_twl(data, mediaType, titleIds[i]) : task_populate_titles_add_ctr(data, mediaType, titleIds[i])) && data->compare != NULL) { + linked_list_sort(data->items, data->userData, data->compare); + } } } } diff --git a/source/ui/section/task/task.h b/source/ui/section/task/task.h index f1ed0ad..e4ccb9e 100644 --- a/source/ui/section/task/task.h +++ b/source/ui/section/task/task.h @@ -75,7 +75,7 @@ typedef struct titledb_info_s { meta_info meta; } titledb_info; -typedef struct { +typedef struct capture_cam_data_s { u16* buffer; s16 width; s16 height; @@ -140,18 +140,19 @@ typedef struct data_op_info_s { Handle cancelEvent; } data_op_data; -typedef struct { +typedef struct populate_ext_save_data_data_s { linked_list* items; + void* userData; bool (*filter)(void* data, u64 extSaveDataId, FS_MediaType mediaType); - void* filterData; + int (*compare)(void* data, const void* p1, const void* p2); volatile bool finished; Result result; Handle cancelEvent; } populate_ext_save_data_data; -typedef struct { +typedef struct populate_files_data_s { linked_list* items; FS_Archive archive; @@ -168,7 +169,7 @@ typedef struct { Handle cancelEvent; } populate_files_data; -typedef struct { +typedef struct populate_pending_titles_data_s { linked_list* items; volatile bool finished; @@ -176,7 +177,7 @@ typedef struct { Handle cancelEvent; } populate_pending_titles_data; -typedef struct { +typedef struct populate_system_save_data_data_s { linked_list* items; volatile bool finished; @@ -184,7 +185,7 @@ typedef struct { Handle cancelEvent; } populate_system_save_data_data; -typedef struct { +typedef struct populate_tickets_data_s { linked_list* items; volatile bool finished; @@ -192,18 +193,19 @@ typedef struct { Handle cancelEvent; } populate_tickets_data; -typedef struct { +typedef struct populate_titles_data_s { linked_list* items; + void* userData; bool (*filter)(void* data, u64 titleId, FS_MediaType mediaType); - void* filterData; + int (*compare)(void* data, const void* p1, const void* p2); volatile bool finished; Result result; Handle cancelEvent; } populate_titles_data; -typedef struct { +typedef struct populate_titledb_data_s { linked_list* items; volatile bool finished; diff --git a/source/ui/section/titles.c b/source/ui/section/titles.c index 5003555..43e53ef 100644 --- a/source/ui/section/titles.c +++ b/source/ui/section/titles.c @@ -1,5 +1,6 @@ #include #include +#include #include <3ds.h> @@ -11,6 +12,7 @@ #include "../ui.h" #include "../../core/linkedlist.h" #include "../../core/screen.h" +#include "../../core/util.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}; @@ -31,6 +33,7 @@ typedef struct { bool showGameCard; bool showSD; bool showNAND; + bool sortByName; bool populated; } titles_data; @@ -115,7 +118,7 @@ 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) { +static void titles_options_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); @@ -126,7 +129,7 @@ static void titles_filters_add_entry(linked_list* items, const char* name, bool* } } -static void titles_filters_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { +static void titles_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { titles_data* listData = (titles_data*) data; if(hidKeysDown() & KEY_B) { @@ -154,14 +157,15 @@ static void titles_filters_update(ui_view* view, void* data, linked_list* items, } 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); + titles_options_add_entry(items, "Show game card", &listData->showGameCard); + titles_options_add_entry(items, "Show SD", &listData->showSD); + titles_options_add_entry(items, "Show NAND", &listData->showNAND); + titles_options_add_entry(items, "Sort by name", &listData->sortByName); } } -static void titles_filters_open(titles_data* data) { - list_display("Filters", "A: Toggle, B: Return", data, titles_filters_update, NULL); +static void titles_options_open(titles_data* data) { + list_display("Options", "A: Toggle, B: Return", data, titles_options_update, NULL); } static void titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) { @@ -191,7 +195,7 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it } if(hidKeysDown() & KEY_SELECT) { - titles_filters_open(listData); + titles_options_open(listData); return; } @@ -236,6 +240,46 @@ static bool titles_filter(void* data, u64 titleId, FS_MediaType mediaType) { } } +static int titles_compare(void* data, const void* p1, const void* p2) { + titles_data* listData = (titles_data*) data; + + list_item* info1 = (list_item*) p1; + list_item* info2 = (list_item*) p2; + + title_info* title1 = (title_info*) info1->data; + title_info* title2 = (title_info*) info2->data; + + if(!title1->twl && title2->twl) { + return -1; + } else if(title1->twl && !title2->twl) { + return 1; + } else { + if(title1->mediaType > title2->mediaType) { + return -1; + } else if(title1->mediaType < title2->mediaType) { + return 1; + } else { + if(listData->sortByName) { + bool title1HasName = title1->hasMeta && !util_is_string_empty(title1->meta.shortDescription); + bool title2HasName = title2->hasMeta && !util_is_string_empty(title2->meta.shortDescription); + + if(title1HasName && !title2HasName) { + return -1; + } else if(!title1HasName && title2HasName) { + return 1; + } else { + return strcasecmp(info1->name, info2->name); + } + } else { + u64 id1 = title1->titleId; + u64 id2 = title2->titleId; + + return id1 > id2 ? 1 : id1 < id2 ? -1 : 0; + } + } + } +} + void titles_open() { titles_data* data = (titles_data*) calloc(1, sizeof(titles_data)); if(data == NULL) { @@ -244,14 +288,16 @@ void titles_open() { return; } + data->populateData.userData = data; data->populateData.filter = titles_filter; - data->populateData.filterData = data; + data->populateData.compare = titles_compare; data->populateData.finished = true; data->showGameCard = true; data->showSD = true; data->showNAND = true; + data->sortByName = true; - list_display("Titles", "A: Select, B: Return, X: Refresh, Select: Filter", data, titles_update, titles_draw_top); + list_display("Titles", "A: Select, B: Return, X: Refresh, Select: Options", data, titles_update, titles_draw_top); } \ No newline at end of file