mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-06 03:58:02 +08:00
Allow sorting titles and ext save data by name.
This commit is contained in:
parent
e8b1d583ec
commit
2b57845edd
@ -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) {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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, "<current directory>", LIST_ITEM_NAME_MAX) == 0 || strncmp(info1->name, "<current file>", LIST_ITEM_NAME_MAX) == 0;
|
||||
bool info2Base = strncmp(info2->name, "<current directory>", LIST_ITEM_NAME_MAX) == 0 || strncmp(info2->name, "<current file>", LIST_ITEM_NAME_MAX) == 0;
|
||||
|
@ -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);
|
||||
|
106
source/ui/list.c
106
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;
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
|
@ -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 {
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
@ -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() {
|
||||
|
@ -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);
|
||||
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,5 +1,6 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#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);
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user