diff --git a/source/core/util.c b/source/core/util.c index ecd98ea..9ecf1ee 100644 --- a/source/core/util.c +++ b/source/core/util.c @@ -7,6 +7,7 @@ #include "util.h" #include "../ui/error.h" +#include "../ui/list.h" #include "../ui/section/task/task.h" extern void cleanup(); @@ -356,6 +357,22 @@ 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; + + file_info* f1 = (file_info*) info1->data; + file_info* f2 = (file_info*) info2->data; + + if(f1->isDirectory && !f2->isDirectory) { + return -1; + } else if(!f1->isDirectory && f2->isDirectory) { + return 1; + } else { + return strcasecmp(f1->name, f2->name); + } +} + static const char* path3dsx = NULL; const char* util_get_3dsx_path() { diff --git a/source/core/util.h b/source/core/util.h index 75862bc..17146b6 100644 --- a/source/core/util.h +++ b/source/core/util.h @@ -62,5 +62,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); + const char* util_get_3dsx_path(); void util_set_3dsx_path(const char* path); \ No newline at end of file diff --git a/source/ui/kbd.c b/source/ui/kbd.c index 39196dd..4717bbe 100644 --- a/source/ui/kbd.c +++ b/source/ui/kbd.c @@ -332,9 +332,9 @@ static void kbd_draw_bottom(ui_view* view, void* data, float x1, float y1, float } } -void kbd_display(const char* name, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), - void (*finished)(void* data, char* input), - void (*canceled)(void* data)) { +void kbd_display(const char* name, const char* initialInput, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), + void (*finished)(void* data, char* input), + void (*canceled)(void* data)) { kbd_data* kbdData = (kbd_data*) calloc(1, sizeof(kbd_data)); if(kbdData == NULL) { error_display(NULL, NULL, NULL, "Failed to allocate info data."); @@ -344,6 +344,11 @@ void kbd_display(const char* name, void* data, void (*drawTop)(ui_view* view, vo memset(kbdData->input, '\0', MAX_INPUT_SIZE); kbdData->inputPos = 0; + if(initialInput != NULL) { + strncpy(kbdData->input, initialInput, MAX_INPUT_SIZE); + kbdData->inputPos = strlen(kbdData->input); + } + kbdData->shift = false; kbdData->capsLock = false; diff --git a/source/ui/kbd.h b/source/ui/kbd.h index bd48dd4..7fc1076 100644 --- a/source/ui/kbd.h +++ b/source/ui/kbd.h @@ -2,6 +2,6 @@ typedef struct ui_view_s ui_view; -void kbd_display(const char* name, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), - void (*finished)(void* data, char* input), - void (*canceled)(void* data)); \ No newline at end of file +void kbd_display(const char* name, const char* initialInput, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), + void (*finished)(void* data, char* input), + void (*canceled)(void* data)); \ No newline at end of file diff --git a/source/ui/section/action/action.h b/source/ui/section/action/action.h index 1273b97..bd927a5 100644 --- a/source/ui/section/action/action.h +++ b/source/ui/section/action/action.h @@ -24,7 +24,9 @@ void action_delete_dir(linked_list* items, list_item* selected); void action_delete_dir_contents(linked_list* items, list_item* selected); void action_delete_dir_cias(linked_list* items, list_item* selected); void action_delete_dir_tickets(linked_list* items, list_item* selected); +void action_new_folder(linked_list* items, list_item* selected); void action_paste_contents(linked_list* items, list_item* selected); +void action_rename(linked_list* items, list_item* selected); void action_delete_pending_title(linked_list* items, list_item* selected); void action_delete_all_pending_titles(linked_list* items, list_item* selected); diff --git a/source/ui/section/action/newfolder.c b/source/ui/section/action/newfolder.c new file mode 100644 index 0000000..7d36f73 --- /dev/null +++ b/source/ui/section/action/newfolder.c @@ -0,0 +1,76 @@ +#include +#include +#include + +#include <3ds.h> + +#include "action.h" +#include "../task/task.h" +#include "../../error.h" +#include "../../info.h" +#include "../../kbd.h" +#include "../../list.h" +#include "../../prompt.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" + +typedef struct { + linked_list* items; + file_info* parentDir; +} new_folder_data; + +static void action_new_folder_kbd_finished(void* data, char* input) { + new_folder_data* newFolderData = (new_folder_data*) data; + + if(strlen(input) == 0) { + error_display(NULL, NULL, NULL, "No name specified."); + } + + Result res = 0; + + char path[FILE_PATH_MAX] = {'\0'}; + snprintf(path, FILE_PATH_MAX, "%s%s", newFolderData->parentDir->path, input); + + FS_Path* fsPath = util_make_path_utf8(path); + if(fsPath != NULL) { + res = FSUSER_CreateDirectory(newFolderData->parentDir->archive, *fsPath, 0); + + util_free_path_utf8(fsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + if(R_SUCCEEDED(res)) { + list_item* folderItem = NULL; + if(R_SUCCEEDED(task_create_file_item(&folderItem, newFolderData->parentDir->archive, path))) { + linked_list_add(newFolderData->items, folderItem); + linked_list_sort(newFolderData->items, util_compare_file_infos); + } + + prompt_display("Success", "Folder created.", COLOR_TEXT, false, NULL, NULL, NULL); + } else { + error_display_res(NULL, NULL, NULL, res, "Failed to create folder."); + } + + free(data); +} + +static void action_new_folder_kbd_canceled(void* data) { + free(data); +} + +void action_new_folder(linked_list* items, list_item* selected) { + new_folder_data* data = (new_folder_data*) calloc(1, sizeof(new_folder_data)); + if(data == NULL) { + error_display(NULL, NULL, NULL, "Failed to allocate new folder data."); + + return; + } + + data->items = items; + data->parentDir = (file_info*) selected->data; + + kbd_display("Enter Name", NULL, data, NULL, action_new_folder_kbd_finished, action_new_folder_kbd_canceled); +} \ No newline at end of file diff --git a/source/ui/section/action/pastefiles.c b/source/ui/section/action/pastefiles.c index 73d24e6..bc115e9 100644 --- a/source/ui/section/action/pastefiles.c +++ b/source/ui/section/action/pastefiles.c @@ -215,29 +215,13 @@ static void action_paste_files_free_data(paste_files_data* data) { free(data); } -static int action_paste_files_compare(const void** p1, const void** p2) { - list_item* info1 = *(list_item**) p1; - list_item* info2 = *(list_item**) p2; - - file_info* f1 = (file_info*) info1->data; - file_info* f2 = (file_info*) info2->data; - - if(f1->isDirectory && !f2->isDirectory) { - return -1; - } else if(!f1->isDirectory && f2->isDirectory) { - return 1; - } else { - return strcasecmp(f1->name, f2->name); - } -} - static void action_paste_files_update(ui_view* view, void* data, float* progress, char* text) { paste_files_data* pasteData = (paste_files_data*) data; if(pasteData->pasteInfo.finished) { FSUSER_ControlArchive(pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0); - linked_list_sort(pasteData->items, action_paste_files_compare); + linked_list_sort(pasteData->items, 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 new file mode 100644 index 0000000..ea0154c --- /dev/null +++ b/source/ui/section/action/rename.c @@ -0,0 +1,92 @@ +#include +#include +#include + +#include <3ds.h> + +#include "action.h" +#include "../task/task.h" +#include "../../error.h" +#include "../../info.h" +#include "../../kbd.h" +#include "../../list.h" +#include "../../prompt.h" +#include "../../ui.h" +#include "../../../core/linkedlist.h" +#include "../../../core/screen.h" +#include "../../../core/util.h" + +typedef struct { + linked_list* items; + list_item* target; +} rename_data; + +static void action_rename_kbd_finished(void* data, char* input) { + rename_data* renameData = (rename_data*) data; + + if(strlen(input) == 0) { + error_display(NULL, NULL, NULL, "No name specified."); + } + + file_info* targetInfo = (file_info*) renameData->target->data; + + Result res = 0; + + char parentPath[FILE_PATH_MAX] = {'\0'}; + util_get_parent_path(parentPath, targetInfo->path, FILE_PATH_MAX); + + char dstPath[FILE_PATH_MAX] = {'\0'}; + snprintf(dstPath, FILE_PATH_MAX, "%s%s", parentPath, input); + + FS_Path* srcFsPath = util_make_path_utf8(targetInfo->path); + if(srcFsPath != NULL) { + FS_Path* dstFsPath = util_make_path_utf8(dstPath); + if(dstFsPath != NULL) { + if(targetInfo->isDirectory) { + res = FSUSER_RenameDirectory(targetInfo->archive, *srcFsPath, targetInfo->archive, *dstFsPath); + } else { + res = FSUSER_RenameFile(targetInfo->archive, *srcFsPath, targetInfo->archive, *dstFsPath); + } + + util_free_path_utf8(dstFsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + util_free_path_utf8(srcFsPath); + } else { + res = R_FBI_OUT_OF_MEMORY; + } + + if(R_SUCCEEDED(res)) { + strncpy(renameData->target->name, input, LIST_ITEM_NAME_MAX); + strncpy(targetInfo->name, input, FILE_NAME_MAX); + strncpy(targetInfo->path, dstPath, FILE_PATH_MAX); + + linked_list_sort(renameData->items, util_compare_file_infos); + + prompt_display("Success", "Renamed.", COLOR_TEXT, false, NULL, NULL, NULL); + } else { + error_display_res(NULL, NULL, NULL, res, "Failed to perform rename."); + } + + free(data); +} + +static void action_rename_kbd_canceled(void* data) { + free(data); +} + +void action_rename(linked_list* items, list_item* selected) { + rename_data* data = (rename_data*) calloc(1, sizeof(rename_data)); + if(data == NULL) { + error_display(NULL, NULL, NULL, "Failed to allocate rename data."); + + return; + } + + data->items = items; + data->target = selected; + + kbd_display("Enter New Name", data->target->name, data, NULL, action_rename_kbd_finished, action_rename_kbd_canceled); +} \ No newline at end of file diff --git a/source/ui/section/files.c b/source/ui/section/files.c index c7c63f6..0cf3d4a 100644 --- a/source/ui/section/files.c +++ b/source/ui/section/files.c @@ -16,6 +16,7 @@ #include "../../core/screen.h" #include "../../core/util.h" +static list_item rename_opt = {"Rename", COLOR_TEXT, action_rename}; static list_item copy = {"Copy", COLOR_TEXT, NULL}; static list_item paste = {"Paste", COLOR_TEXT, action_paste_contents}; @@ -27,6 +28,7 @@ static list_item install_and_delete_cia = {"Install and delete CIA", COLOR_TEXT, static list_item install_ticket = {"Install ticket", COLOR_TEXT, action_install_ticket}; static list_item install_and_delete_ticket = {"Install and delete ticket", COLOR_TEXT, action_install_ticket_delete}; +static list_item new_folder = {"New folder", COLOR_TEXT, action_new_folder}; static list_item delete_dir = {"Delete", COLOR_TEXT, action_delete_dir}; static list_item delete_all_contents = {"Delete all contents", COLOR_TEXT, action_delete_dir_contents}; static list_item copy_all_contents = {"Copy all contents", COLOR_TEXT, NULL}; @@ -120,6 +122,8 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l linked_list_add(items, &delete_all_tickets); } + linked_list_add(items, &new_folder); + linked_list_add(items, &delete_all_contents); linked_list_add(items, ©_all_contents); @@ -138,6 +142,7 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l linked_list_add(items, &delete_file); } + linked_list_add(items, &rename_opt); linked_list_add(items, ©); linked_list_add(items, &paste); } @@ -278,6 +283,11 @@ static void files_free_data(files_data* data) { static void files_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) { files_data* listData = (files_data*) data; + // Detect whether the current directory was renamed by an action. + if(listData->populated && listData->dirItem != NULL && strncmp(listData->currDir, ((file_info*) listData->dirItem->data)->path, FILE_PATH_MAX) != 0) { + strncpy(listData->currDir, ((file_info*) listData->dirItem->data)->path, FILE_PATH_MAX); + } + while(!util_is_dir(listData->archive, listData->currDir)) { char parentDir[FILE_PATH_MAX] = {'\0'}; util_get_parent_path(parentDir, listData->currDir, FILE_PATH_MAX); diff --git a/source/ui/section/qrinstall.c b/source/ui/section/qrinstall.c index 774ec46..3f78a03 100644 --- a/source/ui/section/qrinstall.c +++ b/source/ui/section/qrinstall.c @@ -390,7 +390,7 @@ static void qrinstall_wait_update(ui_view* view, void* data, float* progress, ch } if(hidKeysDown() & KEY_X) { - kbd_display("Enter URL(s)", data, NULL, qrinstall_process_urls, NULL); + kbd_display("Enter URL(s)", NULL, data, NULL, qrinstall_process_urls, NULL); return; }