Allow sorting titles and ext save data by name.

This commit is contained in:
Steveice10 2016-12-17 12:03:06 -08:00
parent e8b1d583ec
commit 2b57845edd
15 changed files with 273 additions and 165 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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