Further cleanup.

This commit is contained in:
Steven Smith 2016-04-28 18:01:51 -07:00
parent e635b6c188
commit 91636d6741
45 changed files with 1513 additions and 1590 deletions

View File

@ -3,4 +3,5 @@ nand=FF0000FF
sd=FF00FF00
gamecard=FFFF0000
dstitle=FF82004B
file=FF000000
directory=FF0000FF

View File

@ -186,23 +186,27 @@ 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)) {
void linked_list_sort(linked_list* list, int (*compare)(const void** p1, const void** p2)) {
unsigned int count = list->size;
void* elements[count];
unsigned int i = 0;
linked_list_node* node = list->first;
while(node != NULL && i < count) {
elements[i++] = node->value;
node = node->next;
}
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_clear(list);
linked_list_clear(list);
qsort(elements, count, sizeof(void*), compare);
qsort(elements, num, sizeof(void*), (int (*)(const void* p1, const void* p2)) compare);
for(unsigned int index = 0; index < count; index++) {
linked_list_add(list, elements[index]);
for(unsigned int i = 0; i < num; i++) {
linked_list_add(list, elements[i]);
}
free(elements);
}
}
@ -240,4 +244,5 @@ void linked_list_iter_remove(linked_list_iter* iter) {
}
linked_list_remove_node(iter->list, iter->curr);
iter->curr = NULL;
}

View File

@ -31,7 +31,7 @@ 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, int (*compare)(const void** p1, const void** p2));
void linked_list_iterate(linked_list* list, linked_list_iter* iter);

View File

@ -199,6 +199,8 @@ void screen_init() {
colorConfig[COLOR_GAME_CARD] = color;
} else if(strcasecmp(key, "dstitle") == 0) {
colorConfig[COLOR_DS_TITLE] = color;
} else if(strcasecmp(key, "file") == 0) {
colorConfig[COLOR_FILE] = color;
} else if(strcasecmp(key, "directory") == 0) {
colorConfig[COLOR_DIRECTORY] = color;
}

View File

@ -41,14 +41,15 @@
#define TEXTURE_WIFI_3 30
#define TEXTURE_AUTO_START 31
#define NUM_COLORS 6
#define NUM_COLORS 7
#define COLOR_TEXT 0
#define COLOR_NAND 1
#define COLOR_SD 2
#define COLOR_GAME_CARD 3
#define COLOR_DS_TITLE 4
#define COLOR_DIRECTORY 5
#define COLOR_FILE 5
#define COLOR_DIRECTORY 6
void screen_init();
void screen_exit();

View File

@ -6,6 +6,7 @@
#include <3ds.h>
#include "util.h"
#include "../ui/error.h"
#include "../ui/section/task/task.h"
extern void cleanup();
@ -152,33 +153,18 @@ void util_free_path_utf8(FS_Path* path) {
free(path);
}
bool util_exists(FS_Archive* archive, const char* path) {
bool exists = false;
FS_Path* fsPath = util_make_path_utf8(path);
if(path != NULL) {
Handle handle = 0;
if(R_SUCCEEDED(FSUSER_OpenFile(&handle, *archive, *fsPath, FS_OPEN_READ, 0))) {
FSFILE_Close(handle);
exists = true;
} else if(R_SUCCEEDED(FSUSER_OpenDirectory(&handle, *archive, *fsPath))) {
FSDIR_Close(handle);
exists = true;
}
util_free_path_utf8(fsPath);
}
return exists;
FS_Path util_make_binary_path(const void* data, u32 size) {
FS_Path path = {PATH_BINARY, size, data};
return path;
}
bool util_is_dir(FS_Archive* archive, const char* path) {
bool util_is_dir(FS_Archive archive, const char* path) {
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, *archive, *fsPath))) {
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, archive, *fsPath))) {
FSDIR_Close(dirHandle);
}
@ -190,19 +176,22 @@ bool util_is_dir(FS_Archive* archive, const char* path) {
return R_SUCCEEDED(res);
}
Result util_ensure_dir(FS_Archive* archive, const char* path) {
Result util_ensure_dir(FS_Archive archive, const char* path) {
Result res = 0;
if(!util_is_dir(archive, path)) {
FS_Path* fsPath = util_make_path_utf8(path);
if(fsPath != NULL) {
FSUSER_DeleteFile(*archive, *fsPath);
res = FSUSER_CreateDirectory(*archive, *fsPath, 0);
util_free_path_utf8(fsPath);
FS_Path* fsPath = util_make_path_utf8(path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(FSUSER_OpenDirectory(&dirHandle, archive, *fsPath))) {
FSDIR_Close(dirHandle);
} else {
res = R_FBI_OUT_OF_MEMORY;
FSUSER_DeleteFile(archive, *fsPath);
res = FSUSER_CreateDirectory(archive, *fsPath, 0);
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
return res;
@ -245,285 +234,4 @@ void util_get_parent_path(char* out, const char* path, u32 size) {
u32 terminatorPos = end - path + 1 < size - 1 ? end - path + 1 : size - 1;
strncpy(out, path, terminatorPos);
out[terminatorPos] = '\0';
}
bool util_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return (bool) (attributes & FS_ATTRIBUTE_DIRECTORY);
}
bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return !(attributes & FS_ATTRIBUTE_DIRECTORY);
}
bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes) {
return !(attributes & FS_ATTRIBUTE_HIDDEN);
}
bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes) {
if(data == NULL) {
return true;
}
char* extension = (char*) data;
size_t extensionLen = strlen(extension);
size_t len = strlen(path);
return util_filter_files(data, archive, path, attributes) && len >= extensionLen && strcmp(path + len - extensionLen, extension) == 0;
}
bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes) {
if(data == NULL) {
return true;
}
return strcmp(path, (char*) data) != 0;
}
static Result util_traverse_dir_internal(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(path);
if(fsPath != NULL) {
Handle handle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&handle, *archive, *fsPath))) {
size_t pathLen = strlen(path);
char* pathBuf = (char*) calloc(1, FILE_PATH_MAX);
if(pathBuf != NULL) {
strncpy(pathBuf, path, FILE_PATH_MAX);
u32 entryCount = 0;
FS_DirectoryEntry entry;
u32 done = 0;
while(R_SUCCEEDED(FSDIR_Read(handle, &entryCount, 1, &entry)) && entryCount > 0) {
ssize_t units = utf16_to_utf8((uint8_t*) pathBuf + pathLen, entry.name, FILE_PATH_MAX - pathLen - 1);
if(units > 0) {
pathBuf[pathLen + units] = '\0';
if(entry.attributes & FS_ATTRIBUTE_DIRECTORY) {
if(pathLen + units < FILE_PATH_MAX - 2) {
pathBuf[pathLen + units] = '/';
pathBuf[pathLen + units + 1] = '\0';
}
}
if(dirsFirst) {
if(process != NULL && (filter == NULL || filter(data, archive, pathBuf, entry.attributes))) {
process(data, archive, pathBuf, entry.attributes);
}
}
if((entry.attributes & FS_ATTRIBUTE_DIRECTORY) && recursive) {
if(R_FAILED(res = util_traverse_dir_internal(archive, pathBuf, recursive, dirsFirst, data, filter, process))) {
break;
}
}
if(!dirsFirst) {
if(process != NULL && (filter == NULL || filter(data, archive, pathBuf, entry.attributes))) {
process(data, archive, pathBuf, entry.attributes);
}
}
}
done++;
}
free(pathBuf);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
FSDIR_Close(handle);
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
return res;
}
static Result util_traverse_dir(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(dirsFirst && strcmp(path, "/") != 0) {
if(process != NULL && (filter == NULL || filter(data, archive, path, FS_ATTRIBUTE_DIRECTORY))) {
process(data, archive, path, FS_ATTRIBUTE_DIRECTORY);
}
}
Result res = util_traverse_dir_internal(archive, path, recursive, dirsFirst, data, filter, process);
if(!dirsFirst && strcmp(path, "/") != 0) {
if(process != NULL && (filter == NULL || filter(data, archive, path, FS_ATTRIBUTE_DIRECTORY))) {
process(data, archive, path, FS_ATTRIBUTE_DIRECTORY);
}
}
return res;
}
static Result util_traverse_file(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(path);
if(fsPath != NULL) {
Handle handle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&handle, *archive, *fsPath, FS_OPEN_READ, 0))) {
if(process != NULL && (filter == NULL || filter(data, archive, path, 0))) {
process(data, archive, path, 0);
}
FSFILE_Close(handle);
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
return res;
}
Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
Result res = 0;
if(util_is_dir(archive, path)) {
res = util_traverse_dir(archive, path, recursive, dirsFirst, data, filter, process);
} else {
res = util_traverse_file(archive, path, recursive, dirsFirst, data, filter, process);
}
return res;
}
typedef struct {
u32* count;
void* data;
bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes);
} count_data;
static bool util_count_contents_filter(void* data, FS_Archive* archive, const char* path, u32 attributes) {
count_data* countData = (count_data*) data;
if(countData->filter != NULL) {
return countData->filter(countData->data, archive, path, attributes);
}
return true;
}
static void util_count_contents_process(void* data, FS_Archive* archive, const char* path, u32 attributes) {
(*((count_data*) data)->count)++;
}
Result util_count_contents(u32* out, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(out == NULL) {
return 0;
}
count_data countData;
countData.count = out;
countData.data = data;
countData.filter = filter;
return util_traverse_contents(archive, path, recursive, dirsFirst, &countData, util_count_contents_filter, util_count_contents_process);
}
typedef struct {
char*** contents;
u32 index;
void* data;
bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes);
} populate_data;
static bool util_populate_contents_filter(void* data, FS_Archive* archive, const char* path, u32 attributes) {
populate_data* populateData = (populate_data*) data;
if(populateData->filter != NULL) {
return populateData->filter(populateData->data, archive, path, attributes);
}
return true;
}
static void util_populate_contents_process(void* data, FS_Archive* archive, const char* path, u32 attributes) {
u32 currPathSize = strlen(path) + 1;
char* currPath = (char*) calloc(1, currPathSize);
if(currPath == NULL) {
return;
}
strncpy(currPath, path, currPathSize);
populate_data* populateData = (populate_data*) data;
(*populateData->contents)[populateData->index++] = currPath;
}
Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
if(contentsOut == NULL || countOut == NULL) {
return 0;
}
util_count_contents(countOut, archive, path, recursive, dirsFirst, data, filter);
*contentsOut = (char**) calloc(*countOut, sizeof(char*));
if(*contentsOut == NULL) {
return R_FBI_OUT_OF_MEMORY;
}
populate_data populateData;
populateData.contents = contentsOut;
populateData.index = 0;
populateData.data = data;
populateData.filter = filter;
Result res = util_traverse_contents(archive, path, recursive, dirsFirst, &populateData, util_populate_contents_filter, util_populate_contents_process);
if(R_FAILED(res)) {
util_free_contents(*contentsOut, *countOut);
}
return res;
}
void util_free_contents(char** contents, u32 count) {
for(u32 i = 0; i < count; i++) {
if(contents[i] != NULL) {
free(contents[i]);
}
}
free(contents);
}
int util_compare_u32(const void* e1, const void* e2) {
u32 id1 = *(u32*) e1;
u32 id2 = *(u32*) e2;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
int util_compare_u64(const void* e1, const void* e2) {
u64 id1 = *(u64*) e1;
u64 id2 = *(u64*) e2;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
int util_compare_directory_entries(const void* e1, const void* e2) {
FS_DirectoryEntry* ent1 = (FS_DirectoryEntry*) e1;
FS_DirectoryEntry* ent2 = (FS_DirectoryEntry*) e2;
if((ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) {
return -1;
} else if(!(ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && (ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) {
return 1;
} else {
char entryName1[0x213] = {'\0'};
utf16_to_utf8((uint8_t*) entryName1, ent1->name, sizeof(entryName1) - 1);
char entryName2[0x213] = {'\0'};
utf16_to_utf8((uint8_t*) entryName2, ent2->name, sizeof(entryName2) - 1);
return strcasecmp(entryName1, entryName2);
}
}

View File

@ -43,25 +43,10 @@ void util_panic(const char* s, ...);
FS_Path* util_make_path_utf8(const char* path);
void util_free_path_utf8(FS_Path* path);
bool util_exists(FS_Archive* archive, const char* path);
bool util_is_dir(FS_Archive* archive, const char* path);
Result util_ensure_dir(FS_Archive* archive, const char* path);
FS_Path util_make_binary_path(const void* data, u32 size);
bool util_is_dir(FS_Archive archive, const char* path);
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_filter_dirs(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_files(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_hidden(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_file_extension(void* data, FS_Archive* archive, const char* path, u32 attributes);
bool util_filter_not_path(void* data, FS_Archive* archive, const char* path, u32 attributes);
Result util_traverse_contents(FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes),
void (*process)(void* data, FS_Archive* archive, const char* path, u32 attributes));
Result util_count_contents(u32* out, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes));
Result util_populate_contents(char*** contentsOut, u32* countOut, FS_Archive* archive, const char* path, bool recursive, bool dirsFirst, void* data, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes));
void util_free_contents(char** contents, u32 count);
int util_compare_u32(const void* e1, const void* e2);
int util_compare_u64(const void* e1, const void* e2);
int util_compare_directory_entries(const void* e1, const void* e2);
void util_get_parent_path(char* out, const char* path, u32 size);

View File

@ -482,6 +482,23 @@ static const char* description_to_string(Result res) {
}
break;
case RM_APPLICATION:
switch(res) {
case R_FBI_CANCELLED:
return "Operation cancelled";
case R_FBI_ERRNO:
return "I/O error";
case R_FBI_HTTP_RESPONSE_CODE:
return "HTTP request returned error";
case R_FBI_WRONG_SYSTEM:
return "Attempted to install an N3DS title on an O3DS";
case R_FBI_INVALID_ARGUMENT:
return "Invalid argument";
case R_FBI_THREAD_CREATE_FAILED:
return "Thread creation failed";
default:
break;
}
default:
break;
}

View File

@ -1,5 +1,14 @@
#pragma once
#define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 1)
#define R_FBI_ERRNO MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 2)
#define R_FBI_HTTP_RESPONSE_CODE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 3)
#define R_FBI_WRONG_SYSTEM MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 4)
#define R_FBI_INVALID_ARGUMENT MAKERESULT(RL_PERMANENT, RS_INVALIDARG, RM_APPLICATION, 5)
#define R_FBI_THREAD_CREATE_FAILED MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 6)
#define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)
typedef struct ui_view_s ui_view;
void error_display(volatile bool* dismissed, void* data, void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2), const char* text, ...);

View File

@ -1,6 +1,5 @@
#pragma once
typedef struct file_info_s file_info;
typedef struct linked_list_s linked_list;
typedef struct list_item_s list_item;
@ -11,22 +10,20 @@ void action_delete_ext_save_data(linked_list* items, list_item* selected);
void action_browse_system_save_data(linked_list* items, list_item* selected);
void action_delete_system_save_data(linked_list* items, list_item* selected);
void action_install_cia(linked_list* items, list_item* selected, file_info* target);
void action_install_cia_delete(linked_list* items, list_item* selected, file_info* target);
void action_install_cias(linked_list* items, list_item* selected, file_info* target);
void action_install_cias_delete(linked_list* items, list_item* selected, file_info* target);
void action_install_ticket(linked_list* items, list_item* selected, file_info* target);
void action_install_ticket_delete(linked_list* items, list_item* selected, file_info* target);
void action_install_tickets(linked_list* items, list_item* selected, file_info* target);
void action_install_tickets_delete(linked_list* items, list_item* selected, file_info* target);
void action_copy_content(linked_list* items, list_item* selected, file_info* target);
void action_copy_contents(linked_list* items, list_item* selected, file_info* target);
void action_delete_contents(linked_list* items, list_item* selected, file_info* target);
void action_delete_dir(linked_list* items, list_item* selected, file_info* target);
void action_delete_dir_contents(linked_list* items, list_item* selected, file_info* target);
void action_delete_dir_cias(linked_list* items, list_item* selected, file_info* target);
void action_delete_dir_tickets(linked_list* items, list_item* selected, file_info* target);
void action_paste_contents(linked_list* items, list_item* selected, file_info* target);
void action_install_cia(linked_list* items, list_item* selected);
void action_install_cia_delete(linked_list* items, list_item* selected);
void action_install_cias(linked_list* items, list_item* selected);
void action_install_cias_delete(linked_list* items, list_item* selected);
void action_install_ticket(linked_list* items, list_item* selected);
void action_install_ticket_delete(linked_list* items, list_item* selected);
void action_install_tickets(linked_list* items, list_item* selected);
void action_install_tickets_delete(linked_list* items, list_item* selected);
void action_delete_file(linked_list* items, list_item* selected);
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_paste_contents(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);

View File

@ -4,11 +4,11 @@
#include "../section.h"
#include "../task/task.h"
#include "../../list.h"
#include "../../../core/util.h"
void action_browse_boss_ext_save_data(linked_list* items, list_item* selected) {
ext_save_data_info* info = (ext_save_data_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)};
FS_Archive archive = {ARCHIVE_BOSS_EXTDATA, {PATH_BINARY, 12, path}};
files_open(archive);
files_open(ARCHIVE_BOSS_EXTDATA, util_make_binary_path(path, sizeof(path)));
}

View File

@ -4,11 +4,11 @@
#include "../section.h"
#include "../task/task.h"
#include "../../list.h"
#include "../../../core/util.h"
void action_browse_system_save_data(linked_list* items, list_item* selected) {
system_save_data_info* info = (system_save_data_info*) selected->data;
u32 path[2] = {MEDIATYPE_NAND, info->systemSaveDataId};
FS_Archive archive = {ARCHIVE_SYSTEM_SAVEDATA, {PATH_BINARY, 8, path}};
files_open(archive);
files_open(ARCHIVE_SYSTEM_SAVEDATA, util_make_binary_path(path, sizeof(path)));
}

View File

@ -4,11 +4,11 @@
#include "../section.h"
#include "../task/task.h"
#include "../../list.h"
#include "../../../core/util.h"
void action_browse_title_save_data(linked_list* items, list_item* selected) {
title_info* info = (title_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF)};
FS_Archive archive = {ARCHIVE_USER_SAVEDATA, {PATH_BINARY, 12, path}};
files_open(archive);
files_open(ARCHIVE_USER_SAVEDATA, util_make_binary_path(path, sizeof(path)));
}

View File

@ -4,11 +4,11 @@
#include "../section.h"
#include "../task/task.h"
#include "../../list.h"
#include "../../../core/util.h"
void action_browse_user_ext_save_data(linked_list* items, list_item* selected) {
ext_save_data_info* info = (ext_save_data_info*) selected->data;
u32 path[3] = {info->mediaType, (u32) (info->extSaveDataId & 0xFFFFFFFF), (u32) ((info->extSaveDataId >> 32) & 0xFFFFFFFF)};
FS_Archive archive = {info->shared ? ARCHIVE_SHARED_EXTDATA : ARCHIVE_EXTDATA, {PATH_BINARY, 12, path}};
files_open(archive);
files_open(info->shared ? ARCHIVE_SHARED_EXTDATA : ARCHIVE_EXTDATA, util_make_binary_path(path, sizeof(path)));
}

View File

@ -8,16 +8,16 @@
static bool clipboard_has = false;
static bool clipboard_contents_only;
static FS_Archive clipboard_archive;
static void* clipboard_archive_path;
static char clipboard_path[FILE_PATH_MAX];
bool clipboard_has_contents() {
return clipboard_has;
}
FS_Archive* clipboard_get_archive() {
return &clipboard_archive;
FS_Archive clipboard_get_archive() {
return clipboard_archive;
}
char* clipboard_get_path() {
@ -28,41 +28,30 @@ bool clipboard_is_contents_only() {
return clipboard_contents_only;
}
Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly) {
Result clipboard_set_contents(FS_ArchiveID archiveId, FS_Path* archivePath, const char* path, bool contentsOnly) {
clipboard_clear();
clipboard_has = true;
clipboard_contents_only = contentsOnly;
clipboard_archive = archive;
strncpy(clipboard_path, path, FILE_PATH_MAX);
if(clipboard_archive.lowPath.size > 0) {
clipboard_archive_path = calloc(1, clipboard_archive.lowPath.size);
if(clipboard_archive_path == NULL) {
clipboard_clear();
return R_FBI_OUT_OF_MEMORY;
}
memcpy(clipboard_archive_path, clipboard_archive.lowPath.data, clipboard_archive.lowPath.size);
clipboard_archive.lowPath.data = clipboard_archive_path;
Result res = 0;
if(R_FAILED(res = FSUSER_OpenArchive(&clipboard_archive, archiveId, *archivePath))) {
clipboard_clear();
}
clipboard_archive.handle = 0;
return FSUSER_OpenArchive(&clipboard_archive);
return res;
}
void clipboard_clear() {
if(clipboard_archive.handle != 0) {
FSUSER_CloseArchive(&clipboard_archive);
clipboard_archive.handle = 0;
}
if(clipboard_archive_path != NULL) {
free(clipboard_archive_path);
clipboard_archive_path = NULL;
}
clipboard_has = false;
clipboard_contents_only = false;
memset(clipboard_path, '\0', FILE_PATH_MAX);
if(clipboard_archive != 0) {
FSUSER_CloseArchive(clipboard_archive);
clipboard_archive = 0;
}
}

View File

@ -2,9 +2,11 @@
#include <stdbool.h>
typedef struct file_info_s file_info;
bool clipboard_has_contents();
FS_Archive* clipboard_get_archive();
FS_Archive clipboard_get_archive();
char* clipboard_get_path();
bool clipboard_is_contents_only();
Result clipboard_set_contents(FS_Archive archive, const char* path, bool contentsOnly);
Result clipboard_set_contents(FS_ArchiveID archiveId, FS_Path* archivePath, const char* path, bool contentsOnly);
void clipboard_clear();

View File

@ -1,29 +0,0 @@
#include <3ds.h>
#include "action.h"
#include "clipboard.h"
#include "../task/task.h"
#include "../../error.h"
#include "../../list.h"
#include "../../prompt.h"
#include "../../ui.h"
#include "../../../core/screen.h"
static void action_copy_contents_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool contentsOnly) {
Result res = 0;
if(R_FAILED(res = clipboard_set_contents(*target->archive, target->path, contentsOnly))) {
error_display_res(NULL, target, ui_draw_file_info, res, "Failed to copy to clipboard.");
return;
}
prompt_display("Success", message, COLOR_TEXT, false, target, NULL, ui_draw_file_info, NULL);
}
void action_copy_content(linked_list* items, list_item* selected, file_info* target) {
action_copy_contents_internal(items, selected, target, "Selected content copied to clipboard.", false);
}
void action_copy_contents(linked_list* items, list_item* selected, file_info* target) {
action_copy_contents_internal(items, selected, target, "Directory contents copied to clipboard.", true);
}

View File

@ -19,30 +19,35 @@ typedef struct {
linked_list* items;
file_info* target;
char** contents;
list_item* curr;
linked_list contents;
data_op_info deleteInfo;
Handle cancelEvent;
data_op_data deleteInfo;
} delete_contents_data;
static void action_delete_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_contents_data* deleteData = (delete_contents_data*) data;
u32 curr = deleteData->deleteInfo.processed;
if(curr < deleteData->deleteInfo.total) {
ui_draw_file_info(view, ((list_item*) linked_list_get(&deleteData->contents, curr))->data, x1, y1, x2, y2);
} else if(deleteData->target != NULL) {
ui_draw_file_info(view, deleteData->target, x1, y1, x2, y2);
}
}
static Result action_delete_contents_delete(void* data, u32 index) {
delete_contents_data* deleteData = (delete_contents_data*) data;
list_item* old = deleteData->curr;
task_create_file_item(&deleteData->curr, deleteData->target->archive, deleteData->contents[index]);
if(old != NULL) {
task_free_file(old);
}
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(deleteData->contents[index]);
file_info* info = (file_info*) ((list_item*) linked_list_get(&deleteData->contents, index))->data;
FS_Path* fsPath = util_make_path_utf8(info->path);
if(fsPath != NULL) {
if(util_is_dir(deleteData->target->archive, deleteData->contents[index])) {
res = FSUSER_DeleteDirectory(*deleteData->target->archive, *fsPath);
if(util_is_dir(deleteData->target->archive, info->path)) {
res = FSUSER_DeleteDirectory(deleteData->target->archive, *fsPath);
} else {
res = FSUSER_DeleteFile(*deleteData->target->archive, *fsPath);
res = FSUSER_DeleteFile(deleteData->target->archive, *fsPath);
}
util_free_path_utf8(fsPath);
@ -51,6 +56,9 @@ static Result action_delete_contents_delete(void* data, u32 index) {
}
if(R_SUCCEEDED(res)) {
deleteData->target->containsCias = false;
deleteData->target->containsTickets = false;
linked_list_iter iter;
linked_list_iterate(deleteData->items, &iter);
@ -58,9 +66,13 @@ static Result action_delete_contents_delete(void* data, u32 index) {
list_item* item = (list_item*) linked_list_iter_next(&iter);
file_info* currInfo = (file_info*) item->data;
if(strcmp(currInfo->path, deleteData->contents[index]) == 0) {
if(strncmp(currInfo->path, info->path, FILE_PATH_MAX) == 0) {
linked_list_iter_remove(&iter);
break;
task_free_file(item);
} else if(currInfo->isCia) {
deleteData->target->containsCias = true;
} else if(currInfo->isTicket) {
deleteData->target->containsTickets = true;
}
}
}
@ -72,11 +84,11 @@ static bool action_delete_contents_error(void* data, u32 index, Result res) {
delete_contents_data* deleteData = (delete_contents_data*) data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, deleteData->target, NULL, ui_draw_file_info, NULL);
prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
volatile bool dismissed = false;
error_display_res(&dismissed, deleteData->curr != NULL ? deleteData->curr->data : deleteData->target, ui_draw_file_info, res, "Failed to delete content.");
error_display_res(&dismissed, data, action_delete_contents_draw_top, res, "Failed to delete content.");
while(!dismissed) {
svcSleepThread(1000000);
@ -86,23 +98,9 @@ static bool action_delete_contents_error(void* data, u32 index, Result res) {
return index < deleteData->deleteInfo.total - 1;
}
static void action_delete_contents_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_contents_data* deleteData = (delete_contents_data*) data;
if(deleteData->curr != NULL) {
ui_draw_file_info(view, ((delete_contents_data*) data)->curr->data, x1, y1, x2, y2);
} else if(deleteData->target != NULL) {
ui_draw_file_info(view, ((delete_contents_data*) data)->target, x1, y1, x2, y2);
}
}
static void action_delete_contents_free_data(delete_contents_data* data) {
if(data->curr != NULL) {
task_free_file(data->curr);
data->curr = NULL;
}
util_free_contents(data->contents, data->deleteInfo.total);
task_clear_files(&data->contents);
linked_list_destroy(&data->contents);
free(data);
}
@ -110,15 +108,13 @@ static void action_delete_contents_update(ui_view* view, void* data, float* prog
delete_contents_data* deleteData = (delete_contents_data*) data;
if(deleteData->deleteInfo.finished) {
if(deleteData->target->archive->id == ARCHIVE_USER_SAVEDATA) {
FSUSER_ControlArchive(*deleteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
}
FSUSER_ControlArchive(deleteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
ui_pop();
info_destroy(view);
if(!deleteData->deleteInfo.premature) {
prompt_display("Success", "Contents deleted.", COLOR_TEXT, false, deleteData->target, NULL, ui_draw_file_info, NULL);
if(R_SUCCEEDED(deleteData->deleteInfo.result)) {
prompt_display("Success", "Contents deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
action_delete_contents_free_data(deleteData);
@ -126,8 +122,8 @@ static void action_delete_contents_update(ui_view* view, void* data, float* prog
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(deleteData->cancelEvent);
if((hidKeysDown() & KEY_B) && !deleteData->deleteInfo.finished) {
svcSignalEvent(deleteData->deleteInfo.cancelEvent);
}
*progress = deleteData->deleteInfo.total > 0 ? (float) deleteData->deleteInfo.processed / (float) deleteData->deleteInfo.total : 0;
@ -138,18 +134,20 @@ static void action_delete_contents_onresponse(ui_view* view, void* data, bool re
delete_contents_data* deleteData = (delete_contents_data*) data;
if(response) {
deleteData->cancelEvent = task_data_op(&deleteData->deleteInfo);
if(deleteData->cancelEvent != 0) {
Result res = task_data_op(&deleteData->deleteInfo);
if(R_SUCCEEDED(res)) {
info_display("Deleting Contents", "Press B to cancel.", true, data, action_delete_contents_update, action_delete_contents_draw_top);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate delete operation.");
error_display_res(NULL, deleteData->target, ui_draw_file_info, res, "Failed to initiate delete operation.");
action_delete_contents_free_data(deleteData);
}
} else {
action_delete_contents_free_data(deleteData);
}
}
static void action_delete_contents_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool recursive, void* filterData, bool (*filter)(void* data, FS_Archive* archive, const char* path, u32 attributes)) {
static void action_delete_contents_internal(linked_list* items, list_item* selected, const char* message, bool recursive, bool includeBase, bool ciasOnly, bool ticketsOnly) {
delete_contents_data* data = (delete_contents_data*) calloc(1, sizeof(delete_contents_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate delete contents data.");
@ -158,7 +156,7 @@ static void action_delete_contents_internal(linked_list* items, list_item* selec
}
data->items = items;
data->target = target;
data->target = (file_info*) selected->data;
data->deleteInfo.data = data;
@ -168,35 +166,67 @@ static void action_delete_contents_internal(linked_list* items, list_item* selec
data->deleteInfo.error = action_delete_contents_error;
data->cancelEvent = 0;
linked_list_init(&data->contents);
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->deleteInfo.total, target->archive, target->path, recursive, false, filterData, filter))) {
error_display_res(NULL, target, ui_draw_file_info, res, "Failed to retrieve content list.");
populate_files_data popData;
popData.items = &data->contents;
popData.base = data->target;
popData.recursive = recursive;
popData.includeBase = includeBase;
popData.dirsFirst = false;
free(data);
Result listRes = task_populate_files(&popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, NULL, listRes, "Failed to initiate content list population.");
action_delete_contents_free_data(data);
return;
}
while(!popData.finished) {
svcSleepThread(1000000);
}
if(R_FAILED(popData.result)) {
error_display_res(NULL, NULL, NULL, popData.result, "Failed to populate content list.");
action_delete_contents_free_data(data);
return;
}
linked_list_iter iter;
linked_list_iterate(&data->contents, &iter);
while(linked_list_iter_has_next(&iter)) {
file_info* info = (file_info*) ((list_item*) linked_list_iter_next(&iter))->data;
if((ciasOnly && (info->isDirectory || !info->isCia)) || (ticketsOnly && (info->isDirectory || !info->isTicket))) {
linked_list_iter_remove(&iter);
}
}
data->deleteInfo.total = linked_list_size(&data->contents);
data->deleteInfo.processed = data->deleteInfo.total;
prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_delete_contents_draw_top, action_delete_contents_onresponse);
}
void action_delete_contents(linked_list* items, list_item* selected, file_info* target) {
action_delete_contents_internal(items, selected, target, "Delete the selected content?", true, NULL, NULL);
void action_delete_file(linked_list* items, list_item* selected) {
action_delete_contents_internal(items, selected, "Delete the selected file?", false, true, false, false);
}
void action_delete_dir(linked_list* items, list_item* selected, file_info* target) {
action_delete_contents_internal(items, selected, target, "Delete the current directory?", true, NULL, NULL);
void action_delete_dir(linked_list* items, list_item* selected) {
action_delete_contents_internal(items, selected, "Delete the current directory?", true, true, false, false);
}
void action_delete_dir_contents(linked_list* items, list_item* selected, file_info* target) {
action_delete_contents_internal(items, selected, target, "Delete all contents of the current directory?", true, target->path, util_filter_not_path);
void action_delete_dir_contents(linked_list* items, list_item* selected) {
action_delete_contents_internal(items, selected, "Delete all contents of the current directory?", true, false, false, false);
}
void action_delete_dir_cias(linked_list* items, list_item* selected, file_info* target) {
action_delete_contents_internal(items, selected, target, "Delete all CIAs in the current directory?", false, ".cia", util_filter_file_extension);
void action_delete_dir_cias(linked_list* items, list_item* selected) {
action_delete_contents_internal(items, selected, "Delete all CIAs in the current directory?", false, false, true, false);
}
void action_delete_dir_tickets(linked_list* items, list_item* selected, file_info* target) {
action_delete_contents_internal(items, selected, target, "Delete all tickets in the current directory?", false, ".tik", util_filter_file_extension);
void action_delete_dir_tickets(linked_list* items, list_item* selected) {
action_delete_contents_internal(items, selected, "Delete all tickets in the current directory?", false, false, false, true);
}

View File

@ -1,5 +1,6 @@
#include <malloc.h>
#include <stdio.h>
#include <string.h>
#include <3ds.h>
@ -17,23 +18,40 @@ typedef struct {
linked_list* items;
list_item* selected;
bool all;
linked_list contents;
data_op_info deleteInfo;
Handle cancelEvent;
data_op_data deleteInfo;
} delete_pending_titles_data;
static void action_delete_pending_titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
u32 index = deleteData->deleteInfo.processed;
if(index < deleteData->deleteInfo.total) {
ui_draw_pending_title_info(view, (pending_title_info*) ((list_item*) linked_list_get(&deleteData->contents, index))->data, x1, y1, x2, y2);
}
}
static Result action_delete_pending_titles_delete(void* data, u32 index) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
list_item* item = deleteData->all ? (list_item*) linked_list_get(deleteData->items, index) : deleteData->selected;
list_item* item = (list_item*) linked_list_get(&deleteData->contents, index);
pending_title_info* info = (pending_title_info*) item->data;
Result res = 0;
if(R_SUCCEEDED(res = AM_DeletePendingTitle(info->mediaType, info->titleId))) {
linked_list_remove(deleteData->items, item);
task_free_pending_title(item);
linked_list_iter iter;
linked_list_iterate(deleteData->items, &iter);
while(linked_list_iter_has_next(&iter)) {
list_item* currItem = (list_item*) linked_list_iter_next(&iter);
if(strncmp(currItem->name, item->name, LIST_ITEM_NAME_MAX) == 0) {
linked_list_iter_remove(&iter);
task_free_file(currItem);
}
}
}
return res;
@ -42,14 +60,12 @@ static Result action_delete_pending_titles_delete(void* data, u32 index) {
static bool action_delete_pending_titles_error(void* data, u32 index, Result res) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
pending_title_info* info = (pending_title_info*) (deleteData->all ? ((list_item*) linked_list_get(deleteData->items, index))->data : deleteData->selected->data);
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_pending_title_info, NULL);
prompt_display("Failure", "Delete cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
volatile bool dismissed = false;
error_display_res(&dismissed, info, ui_draw_pending_title_info, res, "Failed to delete pending title.");
error_display_res(&dismissed, data, action_delete_pending_titles_draw_top, res, "Failed to delete pending title.");
while(!dismissed) {
svcSleepThread(1000000);
@ -59,13 +75,10 @@ static bool action_delete_pending_titles_error(void* data, u32 index, Result res
return index < deleteData->deleteInfo.total - 1;
}
static void action_delete_pending_titles_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
u32 index = deleteData->deleteInfo.processed;
if(index < deleteData->deleteInfo.total) {
ui_draw_pending_title_info(view, (pending_title_info*) (deleteData->all ? ((list_item*) linked_list_get(deleteData->items, index))->data : deleteData->selected->data), x1, y1, x2, y2);
}
static void action_delete_pending_titles_free_data(delete_pending_titles_data* data) {
task_clear_pending_titles(&data->contents);
linked_list_destroy(&data->contents);
free(data);
}
static void action_delete_pending_titles_update(ui_view* view, void* data, float* progress, char* text) {
@ -75,17 +88,17 @@ static void action_delete_pending_titles_update(ui_view* view, void* data, float
ui_pop();
info_destroy(view);
if(!deleteData->deleteInfo.premature) {
if(R_SUCCEEDED(deleteData->deleteInfo.result)) {
prompt_display("Success", "Pending title(s) deleted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
free(deleteData);
action_delete_pending_titles_free_data(deleteData);
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(deleteData->cancelEvent);
if((hidKeysDown() & KEY_B) && !deleteData->deleteInfo.finished) {
svcSignalEvent(deleteData->deleteInfo.cancelEvent);
}
*progress = deleteData->deleteInfo.total > 0 ? (float) deleteData->deleteInfo.processed / (float) deleteData->deleteInfo.total : 0;
@ -96,14 +109,16 @@ static void action_delete_pending_titles_onresponse(ui_view* view, void* data, b
delete_pending_titles_data* deleteData = (delete_pending_titles_data*) data;
if(response) {
deleteData->cancelEvent = task_data_op(&deleteData->deleteInfo);
if(deleteData->cancelEvent != 0) {
Result res = task_data_op(&deleteData->deleteInfo);
if(R_SUCCEEDED(res)) {
info_display("Deleting Pending Title(s)", "Press B to cancel.", true, data, action_delete_pending_titles_update, action_delete_pending_titles_draw_top);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate delete operation.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate delete operation.");
action_delete_pending_titles_free_data(deleteData);
}
} else {
free(deleteData);
action_delete_pending_titles_free_data(deleteData);
}
}
@ -118,19 +133,44 @@ void action_delete_pending_titles(linked_list* items, list_item* selected, const
data->items = items;
data->selected = selected;
data->all = all;
data->deleteInfo.data = data;
data->deleteInfo.op = DATAOP_DELETE;
data->deleteInfo.total = all ? linked_list_size(items) : 1;
data->deleteInfo.delete = action_delete_pending_titles_delete;
data->deleteInfo.error = action_delete_pending_titles_error;
data->cancelEvent = 0;
linked_list_init(&data->contents);
if(all) {
populate_pending_titles_data popData;
popData.items = &data->contents;
Result listRes = task_populate_pending_titles(&popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, NULL, listRes, "Failed to initiate pending title list population.");
action_delete_pending_titles_free_data(data);
return;
}
while(!popData.finished) {
svcSleepThread(1000000);
}
if(R_FAILED(popData.result)) {
error_display_res(NULL, NULL, NULL, popData.result, "Failed to populate pending title list.");
action_delete_pending_titles_free_data(data);
return;
}
} else {
linked_list_add(&data->contents, selected);
}
data->deleteInfo.total = linked_list_size(&data->contents);
data->deleteInfo.processed = data->deleteInfo.total;
prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, !all ? action_delete_pending_titles_draw_top : NULL, action_delete_pending_titles_onresponse);
}

View File

@ -30,9 +30,9 @@ static void action_export_secure_value_update(ui_view* view, void* data, float*
return;
}
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) {
if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/securevalue/"))) {
FS_Archive sdmcArchive = 0;
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) {
if(R_SUCCEEDED(res = util_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(sdmcArchive, "/fbi/securevalue/"))) {
char pathBuf[64];
snprintf(pathBuf, 64, "/fbi/securevalue/%016llX.dat", info->titleId);
@ -51,7 +51,7 @@ static void action_export_secure_value_update(ui_view* view, void* data, float*
}
}
FSUSER_CloseArchive(&sdmcArchive);
FSUSER_CloseArchive(sdmcArchive);
}
}

View File

@ -19,40 +19,43 @@ static void action_extract_smdh_update(ui_view* view, void* data, float* progres
Result res = 0;
static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
u32 archivePath[] = {(u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF), info->mediaType, 0x00000000};
FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};
static const u32 filePath[5] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
u32 archivePath[4] = {(u32) (info->titleId & 0xFFFFFFFF), (u32) ((info->titleId >> 32) & 0xFFFFFFFF), info->mediaType, 0x00000000};
Handle fileHandle;
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
SMDH smdh;
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, util_make_binary_path(archivePath, sizeof(archivePath)), util_make_binary_path(filePath, sizeof(filePath)), FS_OPEN_READ, 0))) {
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
if(smdh != NULL) {
u32 bytesRead = 0;
if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
FS_Archive sdmcArchive = 0;
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, "")))) {
if(R_SUCCEEDED(res = util_ensure_dir(sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(sdmcArchive, "/fbi/smdh/"))) {
char pathBuf[64];
snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId);
u32 bytesRead = 0;
if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
if(R_SUCCEEDED(res = FSUSER_OpenArchive(&sdmcArchive))) {
if(R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/")) && R_SUCCEEDED(res = util_ensure_dir(&sdmcArchive, "/fbi/smdh/"))) {
char pathBuf[64];
snprintf(pathBuf, 64, "/fbi/smdh/%016llX.smdh", info->titleId);
FS_Path* fsPath = util_make_path_utf8(pathBuf);
if(fsPath != NULL) {
Handle smdhHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) {
u32 bytesWritten = 0;
res = FSFILE_Write(smdhHandle, &bytesWritten, 0, smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME);
FSFILE_Close(smdhHandle);
}
FS_Path* fsPath = util_make_path_utf8(pathBuf);
if(fsPath != NULL) {
Handle smdhHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFile(&smdhHandle, sdmcArchive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0))) {
u32 bytesWritten = 0;
res = FSFILE_Write(smdhHandle, &bytesWritten, 0, &smdh, sizeof(SMDH), FS_WRITE_FLUSH | FS_WRITE_UPDATE_TIME);
FSFILE_Close(smdhHandle);
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
FSUSER_CloseArchive(&sdmcArchive);
FSUSER_CloseArchive(sdmcArchive);
}
}
free(smdh);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
FSFILE_Close(fileHandle);

View File

@ -23,9 +23,8 @@ static void action_import_secure_value_update(ui_view* view, void* data, float*
FS_Path* fsPath = util_make_path_utf8(pathBuf);
if(fsPath != NULL) {
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
Handle fileHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, sdmcArchive, *fsPath, FS_OPEN_READ, 0))) {
if(R_SUCCEEDED(res = FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) {
u32 bytesRead = 0;
u64 value = 0;
if(R_SUCCEEDED(res = FSFILE_Read(fileHandle, &bytesRead, 0, &value, sizeof(u64)))) {

View File

@ -24,8 +24,7 @@ typedef struct {
u32 responseCode;
data_op_info installInfo;
Handle cancelEvent;
data_op_data installInfo;
} install_cdn_data;
static Result action_install_cdn_is_src_directory(void* data, u32 index, bool* isDirectory) {
@ -173,7 +172,7 @@ static void action_install_cdn_update(ui_view* view, void* data, float* progress
Result res = 0;
if(!installData->installInfo.premature) {
if(R_SUCCEEDED(installData->installInfo.result)) {
if(R_SUCCEEDED(res = AM_InstallTitleFinish())
&& R_SUCCEEDED(res = AM_CommitImportTitles(((installData->ticket->titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD, 1, false, &installData->ticket->titleId))) {
if(installData->ticket->titleId == 0x0004013800000002 || installData->ticket->titleId == 0x0004013820000002) {
@ -182,7 +181,7 @@ static void action_install_cdn_update(ui_view* view, void* data, float* progress
}
}
if(!installData->installInfo.premature && R_SUCCEEDED(res)) {
if(R_SUCCEEDED(installData->installInfo.result) && R_SUCCEEDED(res)) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, installData->ticket, NULL, ui_draw_ticket_info, NULL);
} else {
AM_InstallTitleAbort();
@ -197,8 +196,8 @@ static void action_install_cdn_update(ui_view* view, void* data, float* progress
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(installData->cancelEvent);
if((hidKeysDown() & KEY_B) && !installData->installInfo.finished) {
svcSignalEvent(installData->installInfo.cancelEvent);
}
*progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0;
@ -209,35 +208,30 @@ static void action_install_cdn_onresponse(ui_view* view, void* data, bool respon
install_cdn_data* installData = (install_cdn_data*) data;
if(response) {
u8 n3ds = false;
if(R_SUCCEEDED(APT_CheckNew3DS(&n3ds)) && !n3ds && ((installData->ticket->titleId >> 28) & 0xF) == 2) {
error_display(NULL, installData->ticket, ui_draw_ticket_info, "Failed to install CDN title.\nAttempted to install N3DS title to O3DS.");
action_install_cdn_free_data(installData);
return;
}
FS_MediaType dest = ((installData->ticket->titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD;
AM_DeleteTitle(dest, installData->ticket->titleId);
if(dest == MEDIATYPE_SD) {
AM_QueryAvailableExternalTitleDatabase(NULL);
}
Result res = 0;
if(R_SUCCEEDED(res = AM_InstallTitleBegin(dest, installData->ticket->titleId, false))) {
installData->cancelEvent = task_data_op(&installData->installInfo);
if(installData->cancelEvent != 0) {
info_display("Installing CDN Title", "Press B to cancel.", true, data, action_install_cdn_update, action_install_cdn_draw_top);
} else {
AM_InstallTitleAbort();
u8 n3ds = false;
if(R_FAILED(APT_CheckNew3DS(&n3ds)) || n3ds || ((installData->ticket->titleId >> 28) & 0xF) != 2) {
FS_MediaType dest = ((installData->ticket->titleId >> 32) & 0x8010) != 0 ? MEDIATYPE_NAND : MEDIATYPE_SD;
AM_DeleteTitle(dest, installData->ticket->titleId);
if(dest == MEDIATYPE_SD) {
AM_QueryAvailableExternalTitleDatabase(NULL);
}
if(R_SUCCEEDED(res = AM_InstallTitleBegin(dest, installData->ticket->titleId, false))) {
if(R_SUCCEEDED(res = task_data_op(&installData->installInfo))) {
info_display("Installing CDN Title", "Press B to cancel.", true, data, action_install_cdn_update, action_install_cdn_draw_top);
} else {
AM_InstallTitleAbort();
}
}
} else {
res = R_FBI_WRONG_SYSTEM;
}
if(R_FAILED(res) || installData->cancelEvent == 0) {
error_display(NULL, installData->ticket, ui_draw_ticket_info, "Failed to initiate CDN title installation.");
if(R_FAILED(res)) {
error_display_res(NULL, installData->ticket, ui_draw_ticket_info, res, "Failed to initiate CDN title installation.");
action_install_cdn_free_data(installData);
}
@ -280,7 +274,5 @@ void action_install_cdn(linked_list* items, list_item* selected) {
data->installInfo.error = action_install_cdn_error;
data->cancelEvent = 0;
prompt_display("Confirmation", "Install the selected title from the CDN?", COLOR_TEXT, true, data, NULL, action_install_cdn_draw_top, action_install_cdn_onresponse);
}

View File

@ -17,21 +17,28 @@
typedef struct {
linked_list* items;
list_item* selected;
file_info* target;
list_item* curr;
linked_list contents;
bool all;
bool delete;
u32 numDeleted;
u64 currTitleId;
data_op_info installInfo;
Handle cancelEvent;
data_op_data installInfo;
} install_cias_data;
static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
install_cias_data* installData = (install_cias_data*) data;
u32 curr = installData->installInfo.processed;
if(curr < installData->installInfo.total) {
ui_draw_file_info(view, ((list_item*) linked_list_get(&installData->contents, curr))->data, x1, y1, x2, y2);
} else if(installData->target != NULL) {
ui_draw_file_info(view, installData->target, x1, y1, x2, y2);
}
}
static Result action_install_cias_is_src_directory(void* data, u32 index, bool* isDirectory) {
*isDirectory = false;
return 0;
@ -44,32 +51,13 @@ static Result action_install_cias_make_dst_directory(void* data, u32 index) {
static Result action_install_cias_open_src(void* data, u32 index, u32* handle) {
install_cias_data* installData = (install_cias_data*) data;
if(installData->all) {
linked_list_iter iter;
linked_list_iterate(installData->items, &iter);
u32 count = 0;
while(linked_list_iter_has_next(&iter) && count < index + 1 - installData->numDeleted) {
list_item* item = linked_list_iter_next(&iter);
file_info* info = (file_info*) item->data;
size_t len = strlen(info->path);
if(len > 4 && strcmp(&info->path[len - 4], ".cia") == 0) {
installData->curr = item;
count++;
}
}
} else {
installData->curr = installData->selected;
}
file_info* info = (file_info*) installData->curr->data;
file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data;
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(info->path);
if(fsPath != NULL) {
res = FSUSER_OpenFile(handle, *info->archive, *fsPath, FS_OPEN_READ, 0);
res = FSUSER_OpenFile(handle, info->archive, *fsPath, FS_OPEN_READ, 0);
util_free_path_utf8(fsPath);
} else {
@ -82,20 +70,33 @@ static Result action_install_cias_open_src(void* data, u32 index, u32* handle) {
static Result action_install_cias_close_src(void* data, u32 index, bool succeeded, u32 handle) {
install_cias_data* installData = (install_cias_data*) data;
file_info* info = (file_info*) installData->curr->data;
file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data;
Result res = 0;
if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) {
FS_Path* fsPath = util_make_path_utf8(info->path);
if(fsPath != NULL) {
if(R_SUCCEEDED(FSUSER_DeleteFile(*info->archive, *fsPath))) {
linked_list_remove(installData->items, installData->curr);
task_free_file(installData->curr);
if(R_SUCCEEDED(FSUSER_DeleteFile(info->archive, *fsPath))) {
installData->target->containsCias = false;
installData->target->containsTickets = false;
installData->curr = NULL;
linked_list_iter iter;
linked_list_iterate(installData->items, &iter);
installData->numDeleted++;
while(linked_list_iter_has_next(&iter)) {
list_item* item = (list_item*) linked_list_iter_next(&iter);
file_info* currInfo = (file_info*) item->data;
if(strncmp(currInfo->path, info->path, FILE_PATH_MAX) == 0) {
linked_list_iter_remove(&iter);
task_free_file(item);
} else if(currInfo->isCia) {
installData->target->containsCias = true;
} else if(currInfo->isTicket) {
installData->target->containsTickets = true;
}
}
}
util_free_path_utf8(fsPath);
@ -173,18 +174,12 @@ static Result action_install_cias_write_dst(void* data, u32 handle, u32* bytesWr
bool action_install_cias_error(void* data, u32 index, Result res) {
install_cias_data* installData = (install_cias_data*) data;
file_info* info = (file_info*) installData->curr->data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL);
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
volatile bool dismissed = false;
if(res == R_FBI_WRONG_SYSTEM) {
error_display(&dismissed, info, ui_draw_file_info, "Failed to install CIA file.\nAttempted to install N3DS title to O3DS.");
} else {
error_display_res(&dismissed, info, ui_draw_file_info, res, "Failed to install CIA file.");
}
error_display_res(&dismissed, data, action_install_cias_draw_top, res, "Failed to install CIA file.");
while(!dismissed) {
svcSleepThread(1000000);
@ -194,34 +189,34 @@ bool action_install_cias_error(void* data, u32 index, Result res) {
return index < installData->installInfo.total - 1;
}
static void action_install_cias_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
install_cias_data* installData = (install_cias_data*) data;
if(installData->curr != NULL) {
ui_draw_file_info(view, installData->curr->data, x1, y1, x2, y2);
} else if(installData->target != NULL) {
ui_draw_file_info(view, installData->target, x1, y1, x2, y2);
}
static void action_install_cias_free_data(install_cias_data* data) {
task_clear_files(&data->contents);
linked_list_destroy(&data->contents);
free(data);
}
static void action_install_cias_update(ui_view* view, void* data, float* progress, char* text) {
install_cias_data* installData = (install_cias_data*) data;
if(installData->installInfo.finished) {
if(installData->delete) {
FSUSER_ControlArchive(installData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
}
ui_pop();
info_destroy(view);
if(!installData->installInfo.premature) {
if(R_SUCCEEDED(installData->installInfo.result)) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
free(installData);
action_install_cias_free_data(installData);
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(installData->cancelEvent);
if((hidKeysDown() & KEY_B) && !installData->installInfo.finished) {
svcSignalEvent(installData->installInfo.cancelEvent);
}
*progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0;
@ -232,20 +227,20 @@ static void action_install_cias_onresponse(ui_view* view, void* data, bool respo
install_cias_data* installData = (install_cias_data*) data;
if(response) {
installData->cancelEvent = task_data_op(&installData->installInfo);
if(installData->cancelEvent != 0) {
Result res = task_data_op(&installData->installInfo);
if(R_SUCCEEDED(res)) {
info_display("Installing CIA(s)", "Press B to cancel.", true, data, action_install_cias_update, action_install_cias_draw_top);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate CIA installation.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate CIA installation.");
free(installData);
action_install_cias_free_data(installData);
}
} else {
free(installData);
action_install_cias_free_data(installData);
}
}
static void action_install_cias_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool all, bool delete) {
static void action_install_cias_internal(linked_list* items, list_item* selected, const char* message, bool delete) {
install_cias_data* data = (install_cias_data*) calloc(1, sizeof(install_cias_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate install CIAs data.");
@ -254,13 +249,10 @@ static void action_install_cias_internal(linked_list* items, list_item* selected
}
data->items = items;
data->selected = selected;
data->target = target;
data->target = (file_info*) selected->data;
data->all = all;
data->delete = delete;
data->numDeleted = 0;
data->currTitleId = 0;
data->installInfo.data = data;
@ -283,40 +275,63 @@ static void action_install_cias_internal(linked_list* items, list_item* selected
data->installInfo.error = action_install_cias_error;
data->cancelEvent = 0;
linked_list_init(&data->contents);
if(all) {
linked_list_iter iter;
linked_list_iterate(data->items, &iter);
populate_files_data popData;
popData.items = &data->contents;
popData.base = data->target;
popData.recursive = false;
popData.includeBase = !data->target->isDirectory;
popData.dirsFirst = false;
while(linked_list_iter_has_next(&iter)) {
list_item* item = linked_list_iter_next(&iter);
file_info* info = (file_info*) item->data;
Result listRes = task_populate_files(&popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, NULL, listRes, "Failed to initiate CIA list population.");
size_t len = strlen(info->path);
if(len > 4 && strcmp(&info->path[len - 4], ".cia") == 0) {
data->installInfo.total++;
}
}
} else {
data->installInfo.total = 1;
action_install_cias_free_data(data);
return;
}
while(!popData.finished) {
svcSleepThread(1000000);
}
if(R_FAILED(popData.result)) {
error_display_res(NULL, NULL, NULL, popData.result, "Failed to populate CIA list.");
action_install_cias_free_data(data);
return;
}
linked_list_iter iter;
linked_list_iterate(&data->contents, &iter);
while(linked_list_iter_has_next(&iter)) {
file_info* info = (file_info*) ((list_item*) linked_list_iter_next(&iter))->data;
if(!info->isCia) {
linked_list_iter_remove(&iter);
}
}
data->installInfo.total = linked_list_size(&data->contents);
data->installInfo.processed = data->installInfo.total;
prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_install_cias_draw_top, action_install_cias_onresponse);
}
void action_install_cia(linked_list* items, list_item* selected, file_info* target) {
action_install_cias_internal(items, selected, target, "Install the selected CIA?", false, false);
void action_install_cia(linked_list* items, list_item* selected) {
action_install_cias_internal(items, selected, "Install the selected CIA?", false);
}
void action_install_cia_delete(linked_list* items, list_item* selected, file_info* target) {
action_install_cias_internal(items, selected, target, "Install and delete the selected CIA?", false, true);
void action_install_cia_delete(linked_list* items, list_item* selected) {
action_install_cias_internal(items, selected, "Install and delete the selected CIA?", true);
}
void action_install_cias(linked_list* items, list_item* selected, file_info* target) {
action_install_cias_internal(items, selected, target, "Install all CIAs in the current directory?", true, false);
void action_install_cias(linked_list* items, list_item* selected) {
action_install_cias_internal(items, selected, "Install all CIAs in the current directory?", false);
}
void action_install_cias_delete(linked_list* items, list_item* selected, file_info* target) {
action_install_cias_internal(items, selected, target, "Install and delete all CIAs in the current directory?", true, true);
void action_install_cias_delete(linked_list* items, list_item* selected) {
action_install_cias_internal(items, selected, "Install and delete all CIAs in the current directory?", true);
}

View File

@ -17,20 +17,26 @@
typedef struct {
linked_list* items;
list_item* selected;
file_info* target;
list_item* curr;
linked_list contents;
bool all;
bool delete;
u32 numDeleted;
data_op_info installInfo;
Handle cancelEvent;
data_op_data installInfo;
} install_tickets_data;
static void action_install_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
install_tickets_data* installData = (install_tickets_data*) data;
u32 curr = installData->installInfo.processed;
if(curr < installData->installInfo.total) {
ui_draw_file_info(view, ((list_item*) linked_list_get(&installData->contents, curr))->data, x1, y1, x2, y2);
} else if(installData->target != NULL) {
ui_draw_file_info(view, installData->target, x1, y1, x2, y2);
}
}
static Result action_install_tickets_is_src_directory(void* data, u32 index, bool* isDirectory) {
*isDirectory = false;
return 0;
@ -43,32 +49,13 @@ static Result action_install_tickets_make_dst_directory(void* data, u32 index) {
static Result action_install_tickets_open_src(void* data, u32 index, u32* handle) {
install_tickets_data* installData = (install_tickets_data*) data;
if(installData->all) {
linked_list_iter iter;
linked_list_iterate(installData->items, &iter);
u32 count = 0;
while(linked_list_iter_has_next(&iter) && count < index + 1 - installData->numDeleted) {
list_item* item = linked_list_iter_next(&iter);
file_info* info = (file_info*) item->data;
size_t len = strlen(info->path);
if(len > 4 && strcmp(&info->path[len - 4], ".tik") == 0) {
installData->curr = item;
count++;
}
}
} else {
installData->curr = installData->selected;
}
file_info* info = (file_info*) installData->curr->data;
file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data;
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(info->path);
if(fsPath != NULL) {
res = FSUSER_OpenFile(handle, *info->archive, *fsPath, FS_OPEN_READ, 0);
res = FSUSER_OpenFile(handle, info->archive, *fsPath, FS_OPEN_READ, 0);
util_free_path_utf8(fsPath);
} else {
@ -81,20 +68,33 @@ static Result action_install_tickets_open_src(void* data, u32 index, u32* handle
static Result action_install_tickets_close_src(void* data, u32 index, bool succeeded, u32 handle) {
install_tickets_data* installData = (install_tickets_data*) data;
file_info* info = (file_info*) installData->curr->data;
file_info* info = (file_info*) ((list_item*) linked_list_get(&installData->contents, index))->data;
Result res = 0;
if(R_SUCCEEDED(res = FSFILE_Close(handle)) && installData->delete && succeeded) {
FS_Path* fsPath = util_make_path_utf8(info->path);
if(fsPath != NULL) {
if(R_SUCCEEDED(FSUSER_DeleteFile(*info->archive, *fsPath))) {
linked_list_remove(installData->items, installData->curr);
task_free_file(installData->curr);
if(R_SUCCEEDED(FSUSER_DeleteFile(info->archive, *fsPath))) {
installData->target->containsCias = false;
installData->target->containsTickets = false;
installData->curr = NULL;
linked_list_iter iter;
linked_list_iterate(installData->items, &iter);
installData->numDeleted++;
while(linked_list_iter_has_next(&iter)) {
list_item* item = (list_item*) linked_list_iter_next(&iter);
file_info* currInfo = (file_info*) item->data;
if(strncmp(currInfo->path, info->path, FILE_PATH_MAX) == 0) {
linked_list_iter_remove(&iter);
task_free_file(item);
} else if(currInfo->isCia) {
installData->target->containsCias = true;
} else if(currInfo->isTicket) {
installData->target->containsTickets = true;
}
}
}
util_free_path_utf8(fsPath);
@ -133,14 +133,12 @@ static Result action_install_tickets_write_dst(void* data, u32 handle, u32* byte
static bool action_install_tickets_error(void* data, u32 index, Result res) {
install_tickets_data* installData = (install_tickets_data*) data;
file_info* info = (file_info*) installData->curr->data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL);
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
volatile bool dismissed = false;
error_display_res(&dismissed, info, ui_draw_file_info, res, "Failed to install ticket.");
error_display_res(&dismissed, data, action_install_tickets_draw_top, res, "Failed to install ticket.");
while(!dismissed) {
svcSleepThread(1000000);
@ -150,34 +148,34 @@ static bool action_install_tickets_error(void* data, u32 index, Result res) {
return index < installData->installInfo.total - 1;
}
static void action_install_tickets_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
install_tickets_data* installData = (install_tickets_data*) data;
if(installData->curr != NULL) {
ui_draw_file_info(view, installData->curr->data, x1, y1, x2, y2);
} else if(installData->target != NULL) {
ui_draw_file_info(view, installData->target, x1, y1, x2, y2);
}
static void action_install_tickets_free_data(install_tickets_data* data) {
task_clear_files(&data->contents);
linked_list_destroy(&data->contents);
free(data);
}
static void action_install_tickets_update(ui_view* view, void* data, float* progress, char* text) {
install_tickets_data* installData = (install_tickets_data*) data;
if(installData->installInfo.finished) {
if(installData->delete) {
FSUSER_ControlArchive(installData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
}
ui_pop();
info_destroy(view);
if(!installData->installInfo.premature) {
if(R_SUCCEEDED(installData->installInfo.result)) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
free(installData);
action_install_tickets_free_data(installData);
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(installData->cancelEvent);
if((hidKeysDown() & KEY_B) && !installData->installInfo.finished) {
svcSignalEvent(installData->installInfo.cancelEvent);
}
*progress = installData->installInfo.currTotal != 0 ? (float) ((double) installData->installInfo.currProcessed / (double) installData->installInfo.currTotal) : 0;
@ -188,20 +186,20 @@ static void action_install_tickets_onresponse(ui_view* view, void* data, bool re
install_tickets_data* installData = (install_tickets_data*) data;
if(response) {
installData->cancelEvent = task_data_op(&installData->installInfo);
if(installData->cancelEvent != 0) {
Result res = task_data_op(&installData->installInfo);
if(R_SUCCEEDED(res)) {
info_display("Installing ticket(s)", "Press B to cancel.", true, data, action_install_tickets_update, action_install_tickets_draw_top);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate ticket installation.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate ticket installation.");
free(installData);
action_install_tickets_free_data(installData);
}
} else {
free(installData);
action_install_tickets_free_data(installData);
}
}
static void action_install_tickets_internal(linked_list* items, list_item* selected, file_info* target, const char* message, bool all, bool delete) {
static void action_install_tickets_internal(linked_list* items, list_item* selected, const char* message, bool delete) {
install_tickets_data* data = (install_tickets_data*) calloc(1, sizeof(install_tickets_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate install tickets data.");
@ -210,14 +208,10 @@ static void action_install_tickets_internal(linked_list* items, list_item* selec
}
data->items = items;
data->selected = selected;
data->target = target;
data->target = (file_info*) selected->data;
data->all = all;
data->delete = delete;
data->numDeleted = 0;
data->installInfo.data = data;
data->installInfo.op = DATAOP_COPY;
@ -238,40 +232,63 @@ static void action_install_tickets_internal(linked_list* items, list_item* selec
data->installInfo.error = action_install_tickets_error;
data->cancelEvent = 0;
linked_list_init(&data->contents);
if(all) {
linked_list_iter iter;
linked_list_iterate(data->items, &iter);
populate_files_data popData;
popData.items = &data->contents;
popData.base = data->target;
popData.recursive = false;
popData.includeBase = !data->target->isDirectory;
popData.dirsFirst = false;
while(linked_list_iter_has_next(&iter)) {
list_item* item = linked_list_iter_next(&iter);
file_info* info = (file_info*) item->data;
Result listRes = task_populate_files(&popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, NULL, listRes, "Failed to initiate ticket file list population.");
size_t len = strlen(info->path);
if(len > 4 && strcmp(&info->path[len - 4], ".tik") == 0) {
data->installInfo.total++;
}
}
} else {
data->installInfo.total = 1;
action_install_tickets_free_data(data);
return;
}
while(!popData.finished) {
svcSleepThread(1000000);
}
if(R_FAILED(popData.result)) {
error_display_res(NULL, NULL, NULL, popData.result, "Failed to populate ticket file list.");
action_install_tickets_free_data(data);
return;
}
linked_list_iter iter;
linked_list_iterate(&data->contents, &iter);
while(linked_list_iter_has_next(&iter)) {
file_info* info = (file_info*) ((list_item*) linked_list_iter_next(&iter))->data;
if(!info->isTicket) {
linked_list_iter_remove(&iter);
}
}
data->installInfo.total = linked_list_size(&data->contents);
data->installInfo.processed = data->installInfo.total;
prompt_display("Confirmation", message, COLOR_TEXT, true, data, NULL, action_install_tickets_draw_top, action_install_tickets_onresponse);
}
void action_install_ticket(linked_list* items, list_item* selected, file_info* target) {
action_install_tickets_internal(items, selected, target, "Install the selected ticket?", false, false);
void action_install_ticket(linked_list* items, list_item* selected) {
action_install_tickets_internal(items, selected, "Install the selected ticket?", false);
}
void action_install_ticket_delete(linked_list* items, list_item* selected, file_info* target) {
action_install_tickets_internal(items, selected, target, "Install and delete the selected ticket?", false, true);
void action_install_ticket_delete(linked_list* items, list_item* selected) {
action_install_tickets_internal(items, selected, "Install and delete the selected ticket?", true);
}
void action_install_tickets(linked_list* items, list_item* selected, file_info* target) {
action_install_tickets_internal(items, selected, target, "Install all tickets in the current directory?", true, false);
void action_install_tickets(linked_list* items, list_item* selected) {
action_install_tickets_internal(items, selected, "Install all tickets in the current directory?", false);
}
void action_install_tickets_delete(linked_list* items, list_item* selected, file_info* target) {
action_install_tickets_internal(items, selected, target, "Install and delete all tickets in the current directory?", true, true);
void action_install_tickets_delete(linked_list* items, list_item* selected) {
action_install_tickets_internal(items, selected, "Install and delete all tickets in the current directory?", true);
}

View File

@ -20,15 +20,31 @@ typedef struct {
linked_list* items;
file_info* target;
char** contents;
list_item* currSrc;
linked_list contents;
bool currExists;
data_op_info pasteInfo;
Handle cancelEvent;
data_op_data pasteInfo;
} paste_files_data;
static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
paste_files_data* pasteData = (paste_files_data*) data;
u32 curr = pasteData->pasteInfo.processed;
if(curr < pasteData->pasteInfo.total) {
ui_draw_file_info(view, ((list_item*) linked_list_get(&pasteData->contents, curr))->data, x1, y1, x2, y2);
} else if(pasteData->target != NULL) {
ui_draw_file_info(view, pasteData->target, x1, y1, x2, y2);
}
}
static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, char* dstPath) {
char baseSrcPath[FILE_PATH_MAX];
if(clipboard_is_contents_only()) {
strncpy(baseSrcPath, clipboard_get_path(), FILE_PATH_MAX);
} else {
util_get_parent_path(baseSrcPath, clipboard_get_path(), FILE_PATH_MAX);
}
char baseDstPath[FILE_PATH_MAX];
if(data->target->isDirectory) {
strncpy(baseDstPath, data->target->path, FILE_PATH_MAX);
@ -36,49 +52,33 @@ static void action_paste_files_get_dst_path(paste_files_data* data, u32 index, c
util_get_parent_path(baseDstPath, data->target->path, FILE_PATH_MAX);
}
util_get_parent_path(dstPath, clipboard_get_path(), FILE_PATH_MAX);
snprintf(dstPath, FILE_PATH_MAX, "%s%s", baseDstPath, data->contents[index] + strlen(dstPath));
snprintf(dstPath, FILE_PATH_MAX, "%s%s", baseDstPath, ((file_info*) ((list_item*) linked_list_get(&data->contents, index))->data)->path + strlen(baseSrcPath));
}
static Result action_paste_files_is_src_directory(void* data, u32 index, bool* isDirectory) {
paste_files_data* pasteData = (paste_files_data*) data;
*isDirectory = util_is_dir(clipboard_get_archive(), pasteData->contents[index]);
*isDirectory = ((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->isDirectory;
return 0;
}
static Result action_paste_files_make_dst_directory(void* data, u32 index) {
paste_files_data* pasteData = (paste_files_data*) data;
list_item* old = pasteData->currSrc;
task_create_file_item(&pasteData->currSrc, clipboard_get_archive(), pasteData->contents[index]);
if(old != NULL) {
task_free_file(old);
}
Result res = 0;
char dstPath[FILE_PATH_MAX];
action_paste_files_get_dst_path(pasteData, index, dstPath);
bool existed = util_exists(pasteData->target->archive, dstPath);
if(R_SUCCEEDED(res = util_ensure_dir(pasteData->target->archive, dstPath))) {
char parentPath[FILE_PATH_MAX];
util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX);
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(dstPath);
if(fsPath != NULL) {
res = FSUSER_CreateDirectory(*pasteData->target->archive, *fsPath, 0);
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
char parentPath[FILE_PATH_MAX];
util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX);
if(!existed && strcmp(parentPath, pasteData->target->path) == 0) {
list_item* dstItem = NULL;
if(R_SUCCEEDED(res) && R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath))) {
linked_list_add(pasteData->items, dstItem);
if(strncmp(parentPath, pasteData->target->path, FILE_PATH_MAX) == 0) {
list_item* dstItem = NULL;
if(R_SUCCEEDED(res) && R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath))) {
linked_list_add(pasteData->items, dstItem);
}
}
}
@ -88,17 +88,11 @@ static Result action_paste_files_make_dst_directory(void* data, u32 index) {
static Result action_paste_files_open_src(void* data, u32 index, u32* handle) {
paste_files_data* pasteData = (paste_files_data*) data;
list_item* old = pasteData->currSrc;
task_create_file_item(&pasteData->currSrc, clipboard_get_archive(), pasteData->contents[index]);
if(old != NULL) {
task_free_file(old);
}
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(pasteData->contents[index]);
FS_Path* fsPath = util_make_path_utf8(((file_info*) ((list_item*) linked_list_get(&pasteData->contents, index))->data)->path);
if(fsPath != NULL) {
res = FSUSER_OpenFile(handle, *clipboard_get_archive(), *fsPath, FS_OPEN_READ, 0);
res = FSUSER_OpenFile(handle, clipboard_get_archive(), *fsPath, FS_OPEN_READ, 0);
util_free_path_utf8(fsPath);
} else {
@ -123,16 +117,20 @@ static Result action_paste_files_read_src(void* data, u32 handle, u32* bytesRead
static Result action_paste_files_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) {
paste_files_data* pasteData = (paste_files_data*) data;
Result res = 0;
char dstPath[FILE_PATH_MAX];
action_paste_files_get_dst_path(pasteData, index, dstPath);
pasteData->currExists = util_exists(pasteData->target->archive, dstPath);
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(dstPath);
if(fsPath != NULL) {
res = FSUSER_OpenFile(handle, *pasteData->target->archive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0);
Handle currHandle;
pasteData->currExists = R_SUCCEEDED(FSUSER_OpenFile(&currHandle, pasteData->target->archive, *fsPath, FS_OPEN_READ, 0));
if(pasteData->currExists) {
FSFILE_Close(currHandle);
}
res = FSUSER_OpenFile(handle, pasteData->target->archive, *fsPath, FS_OPEN_WRITE | FS_OPEN_CREATE, 0);
util_free_path_utf8(fsPath);
} else {
@ -145,19 +143,27 @@ static Result action_paste_files_open_dst(void* data, u32 index, void* initialRe
static Result action_paste_files_close_dst(void* data, u32 index, bool succeeded, u32 handle) {
paste_files_data* pasteData = (paste_files_data*) data;
Result res = FSFILE_Close(handle);
Result res = 0;
if(R_SUCCEEDED(res) && !pasteData->currExists) {
if(R_SUCCEEDED(res = FSFILE_Close(handle)) && !pasteData->currExists) {
char dstPath[FILE_PATH_MAX];
action_paste_files_get_dst_path(pasteData, index, dstPath);
char parentPath[FILE_PATH_MAX];
util_get_parent_path(parentPath, dstPath, FILE_PATH_MAX);
if(strcmp(parentPath, pasteData->target->path) == 0) {
if(strncmp(parentPath, pasteData->target->path, FILE_PATH_MAX) == 0) {
list_item* dstItem = NULL;
if(R_SUCCEEDED(task_create_file_item(&dstItem, pasteData->target->archive, dstPath))) {
linked_list_add(pasteData->items, dstItem);
file_info* dstInfo = (file_info*) dstItem->data;
if(dstInfo->isCia) {
pasteData->target->containsCias = true;
} else if(dstInfo->isTicket) {
pasteData->target->containsTickets = true;
}
}
}
}
@ -173,13 +179,11 @@ static bool action_paste_files_error(void* data, u32 index, Result res) {
paste_files_data* pasteData = (paste_files_data*) data;
if(res == R_FBI_CANCELLED) {
prompt_display("Failure", "Paste cancelled.", COLOR_TEXT, false, pasteData->target, NULL, ui_draw_file_info, NULL);
prompt_display("Failure", "Paste cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return false;
} else {
char* path = pasteData->contents[index];
volatile bool dismissed = false;
error_display_res(&dismissed, pasteData->currSrc != NULL ? pasteData->currSrc->data : pasteData->target, ui_draw_file_info, res, "Failed to paste content.", path);
error_display_res(&dismissed, data, action_paste_files_draw_top, res, "Failed to paste content.");
while(!dismissed) {
svcSleepThread(1000000);
@ -189,51 +193,41 @@ static bool action_paste_files_error(void* data, u32 index, Result res) {
return index < pasteData->pasteInfo.total - 1;
}
static void action_paste_files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
paste_files_data* pasteData = (paste_files_data*) data;
if(pasteData->currSrc != NULL) {
ui_draw_file_info(view, ((paste_files_data*) data)->currSrc->data, x1, y1, x2, y2);
} else if(pasteData->target != NULL) {
ui_draw_file_info(view, ((paste_files_data*) data)->target, x1, y1, x2, y2);
}
}
static void action_paste_files_free_data(paste_files_data* data) {
if(data->currSrc != NULL) {
task_free_file(data->currSrc);
data->currSrc = NULL;
}
util_free_contents(data->contents, data->pasteInfo.total);
task_clear_files(&data->contents);
linked_list_destroy(&data->contents);
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;
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;
return strcasecmp(f1->name, f2->name);
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) {
if(pasteData->target->archive->id == ARCHIVE_USER_SAVEDATA) {
FSUSER_ControlArchive(*pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
}
FSUSER_ControlArchive(pasteData->target->archive, ARCHIVE_ACTION_COMMIT_SAVE_DATA, NULL, 0, NULL, 0);
linked_list_sort(pasteData->items, action_paste_files_compare);
ui_pop();
info_destroy(view);
if(!pasteData->pasteInfo.premature) {
prompt_display("Success", "Contents pasted.", COLOR_TEXT, false, pasteData->target, NULL, ui_draw_file_info, NULL);
if(R_SUCCEEDED(pasteData->pasteInfo.result)) {
prompt_display("Success", "Contents pasted.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
action_paste_files_free_data(pasteData);
@ -241,8 +235,8 @@ static void action_paste_files_update(ui_view* view, void* data, float* progress
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(pasteData->cancelEvent);
if((hidKeysDown() & KEY_B) && !pasteData->pasteInfo.finished) {
svcSignalEvent(pasteData->pasteInfo.cancelEvent);
}
*progress = pasteData->pasteInfo.currTotal != 0 ? (float) ((double) pasteData->pasteInfo.currProcessed / (double) pasteData->pasteInfo.currTotal) : 0;
@ -252,18 +246,20 @@ static void action_paste_files_update(ui_view* view, void* data, float* progress
static void action_paste_files_onresponse(ui_view* view, void* data, bool response) {
paste_files_data* pasteData = (paste_files_data*) data;
if(response) {
pasteData->cancelEvent = task_data_op(&pasteData->pasteInfo);
if(pasteData->cancelEvent != 0) {
Result res = task_data_op(&pasteData->pasteInfo);
if(R_SUCCEEDED(res)) {
info_display("Pasting Contents", "Press B to cancel.", true, data, action_paste_files_update, action_paste_files_draw_top);
} else {
error_display(NULL, pasteData->target, ui_draw_file_info, "Failed to initiate paste operation.");
error_display_res(NULL, pasteData->target, ui_draw_file_info, res, "Failed to initiate paste operation.");
action_paste_files_free_data(pasteData);
}
} else {
action_paste_files_free_data(pasteData);
}
}
void action_paste_contents(linked_list* items, list_item* selected, file_info* target) {
void action_paste_contents(linked_list* items, list_item* selected) {
if(!clipboard_has_contents()) {
prompt_display("Failure", "Clipboard empty.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
return;
@ -277,9 +273,7 @@ void action_paste_contents(linked_list* items, list_item* selected, file_info* t
}
data->items = items;
data->target = target;
data->currSrc = NULL;
data->target = (file_info*) selected->data;
data->pasteInfo.data = data;
@ -301,15 +295,48 @@ void action_paste_contents(linked_list* items, list_item* selected, file_info* t
data->pasteInfo.error = action_paste_files_error;
data->cancelEvent = 0;
list_item* clipboardItem = NULL;
Result createRes = 0;
if(R_FAILED(createRes = task_create_file_item(&clipboardItem, clipboard_get_archive(), clipboard_get_path()))) {
error_display_res(NULL, NULL, NULL, createRes, "Failed to retrieve clipboard content info.");
Result res = 0;
if(R_FAILED(res = util_populate_contents(&data->contents, &data->pasteInfo.total, clipboard_get_archive(), clipboard_get_path(), true, true, NULL, NULL))) {
error_display_res(NULL, data->target, ui_draw_file_info, res, "Failed to retrieve content list.");
free(data);
action_paste_files_free_data(data);
return;
}
linked_list_init(&data->contents);
populate_files_data popData;
popData.items = &data->contents;
popData.base = (file_info*) clipboardItem->data;
popData.recursive = true;
popData.includeBase = !clipboard_is_contents_only() || !util_is_dir(clipboard_get_archive(), clipboard_get_path());
popData.dirsFirst = true;
Result listRes = task_populate_files(&popData);
if(R_FAILED(listRes)) {
error_display_res(NULL, NULL, NULL, listRes, "Failed to initiate clipboard content list population.");
task_free_file(clipboardItem);
action_paste_files_free_data(data);
return;
}
while(!popData.finished) {
svcSleepThread(1000000);
}
task_free_file(clipboardItem);
if(R_FAILED(popData.result)) {
error_display_res(NULL, NULL, NULL, popData.result, "Failed to populate clipboard content list.");
action_paste_files_free_data(data);
return;
}
data->pasteInfo.total = linked_list_size(&data->contents);
data->pasteInfo.processed = data->pasteInfo.total;
prompt_display("Confirmation", "Paste clipboard contents to the current directory?", COLOR_TEXT, true, data, NULL, action_paste_files_draw_top, action_paste_files_onresponse);
}

View File

@ -11,11 +11,6 @@
#include "../ui.h"
#include "../../core/screen.h"
typedef struct {
data_op_info dumpInfo;
Handle cancelEvent;
} dump_nand_data;
static Result dumpnand_is_src_directory(void* data, u32 index, bool* isDirectory) {
*isDirectory = false;
return 0;
@ -26,8 +21,7 @@ static Result dumpnand_make_dst_directory(void* data, u32 index) {
}
static Result dumpnand_open_src(void* data, u32 index, u32* handle) {
FS_Archive wnandArchive = {ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, "")};
return FSUSER_OpenFileDirectly(handle, wnandArchive, fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0);
return FSUSER_OpenFileDirectly(handle, ARCHIVE_NAND_W_FS, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_UTF16, u"/"), FS_OPEN_READ, 0);
}
static Result dumpnand_close_src(void* data, u32 index, bool succeeded, u32 handle) {
@ -43,8 +37,7 @@ static Result dumpnand_read_src(void* data, u32 handle, u32* bytesRead, void* bu
}
static Result dumpnand_open_dst(void* data, u32 index, void* initialReadBlock, u32* handle) {
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (u8*) ""}};
return FSUSER_OpenFileDirectly(handle, sdmcArchive, fsMakePath(PATH_UTF16, u"/NAND.bin"), FS_OPEN_WRITE | FS_OPEN_CREATE, 0);
return FSUSER_OpenFileDirectly(handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), fsMakePath(PATH_UTF16, u"/NAND.bin"), FS_OPEN_WRITE | FS_OPEN_CREATE, 0);
}
static Result dumpnand_close_dst(void* data, u32 index, bool succeeded, u32 handle) {
@ -66,13 +59,13 @@ static bool dumpnand_error(void* data, u32 index, Result res) {
}
static void dumpnand_update(ui_view* view, void* data, float* progress, char* text) {
dump_nand_data* dumpData = (dump_nand_data*) data;
data_op_data* dumpData = (data_op_data*) data;
if(dumpData->dumpInfo.finished) {
if(dumpData->finished) {
ui_pop();
info_destroy(view);
if(!dumpData->dumpInfo.premature) {
if(R_SUCCEEDED(dumpData->result)) {
prompt_display("Success", "NAND dumped.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
}
@ -85,19 +78,19 @@ static void dumpnand_update(ui_view* view, void* data, float* progress, char* te
svcSignalEvent(dumpData->cancelEvent);
}
*progress = dumpData->dumpInfo.currTotal != 0 ? (float) ((double) dumpData->dumpInfo.currProcessed / (double) dumpData->dumpInfo.currTotal) : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->dumpInfo.currProcessed / 1024.0f / 1024.0f, dumpData->dumpInfo.currTotal / 1024.0f / 1024.0f);
*progress = dumpData->currTotal != 0 ? (float) ((double) dumpData->currProcessed / (double) dumpData->currTotal) : 0;
snprintf(text, PROGRESS_TEXT_MAX, "%.2f MB / %.2f MB", dumpData->currProcessed / 1024.0f / 1024.0f, dumpData->currTotal / 1024.0f / 1024.0f);
}
static void dumpnand_onresponse(ui_view* view, void* data, bool response) {
if(response) {
dump_nand_data* dumpData = (dump_nand_data*) data;
data_op_data* dumpData = (data_op_data*) data;
dumpData->cancelEvent = task_data_op(&dumpData->dumpInfo);
if(dumpData->cancelEvent != 0) {
Result res = task_data_op(dumpData);
if(R_SUCCEEDED(res)) {
info_display("Dumping NAND", "Press B to cancel.", true, data, dumpnand_update, NULL);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate NAND dump.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate NAND dump.");
}
} else {
free(data);
@ -105,36 +98,34 @@ static void dumpnand_onresponse(ui_view* view, void* data, bool response) {
}
void dumpnand_open() {
dump_nand_data* data = (dump_nand_data*) calloc(1, sizeof(dump_nand_data));
data_op_data* data = (data_op_data*) calloc(1, sizeof(data_op_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate dump NAND data.");
return;
}
data->dumpInfo.data = data;
data->data = data;
data->dumpInfo.op = DATAOP_COPY;
data->op = DATAOP_COPY;
data->dumpInfo.copyEmpty = true;
data->copyEmpty = true;
data->dumpInfo.total = 1;
data->total = 1;
data->dumpInfo.isSrcDirectory = dumpnand_is_src_directory;
data->dumpInfo.makeDstDirectory = dumpnand_make_dst_directory;
data->isSrcDirectory = dumpnand_is_src_directory;
data->makeDstDirectory = dumpnand_make_dst_directory;
data->dumpInfo.openSrc = dumpnand_open_src;
data->dumpInfo.closeSrc = dumpnand_close_src;
data->dumpInfo.getSrcSize = dumpnand_get_src_size;
data->dumpInfo.readSrc = dumpnand_read_src;
data->openSrc = dumpnand_open_src;
data->closeSrc = dumpnand_close_src;
data->getSrcSize = dumpnand_get_src_size;
data->readSrc = dumpnand_read_src;
data->dumpInfo.openDst = dumpnand_open_dst;
data->dumpInfo.closeDst = dumpnand_close_dst;
data->dumpInfo.writeDst = dumpnand_write_dst;
data->openDst = dumpnand_open_dst;
data->closeDst = dumpnand_close_dst;
data->writeDst = dumpnand_write_dst;
data->dumpInfo.error = dumpnand_error;
data->cancelEvent = 0;
data->error = dumpnand_error;
prompt_display("Confirmation", "Dump raw NAND image to the SD card?", COLOR_TEXT, true, data, NULL, NULL, dumpnand_onresponse);
}

View File

@ -17,7 +17,8 @@ static list_item browse_spotpass_save_data = {"Browse SpotPass Save Data", COLOR
static list_item delete_save_data = {"Delete Save Data", COLOR_TEXT, action_delete_ext_save_data};
typedef struct {
Handle cancelEvent;
populate_ext_save_data_data populateData;
bool populated;
} extsavedata_data;
@ -86,13 +87,11 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li
extsavedata_data* listData = (extsavedata_data*) data;
if(hidKeysDown() & KEY_B) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
ui_pop();
@ -105,19 +104,28 @@ static void extsavedata_update(ui_view* view, void* data, linked_list* items, li
}
if(!listData->populated || (hidKeysDown() & KEY_X)) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
listData->cancelEvent = task_populate_ext_save_data(items);
listData->populateData.items = items;
Result res = task_populate_ext_save_data(&listData->populateData);
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate ext save data list population.");
}
listData->populated = true;
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate ext save data list.");
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
extsavedata_action_open(items, selected);
return;
@ -132,5 +140,7 @@ void extsavedata_open() {
return;
}
data->populateData.finished = true;
list_display("Ext Save Data", "A: Select, B: Return, X: Refresh", data, extsavedata_update, extsavedata_draw_top);
}

View File

@ -6,18 +6,20 @@
#include "section.h"
#include "action/action.h"
#include "action/clipboard.h"
#include "task/task.h"
#include "../error.h"
#include "../list.h"
#include "../prompt.h"
#include "../ui.h"
#include "../../core/linkedlist.h"
#include "../../core/screen.h"
#include "../../core/util.h"
static list_item copy = {"Copy", COLOR_TEXT, action_copy_content};
static list_item copy = {"Copy", COLOR_TEXT, NULL};
static list_item paste = {"Paste", COLOR_TEXT, action_paste_contents};
static list_item delete_file = {"Delete", COLOR_TEXT, action_delete_contents};
static list_item delete_file = {"Delete", COLOR_TEXT, action_delete_file};
static list_item install_cia = {"Install CIA", COLOR_TEXT, action_install_cia};
static list_item install_and_delete_cia = {"Install and delete CIA", COLOR_TEXT, action_install_cia_delete};
@ -27,7 +29,7 @@ static list_item install_and_delete_ticket = {"Install and delete ticket", COLOR
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, action_copy_contents};
static list_item copy_all_contents = {"Copy all contents", COLOR_TEXT, NULL};
static list_item install_all_cias = {"Install all CIAs", COLOR_TEXT, action_install_cias};
static list_item install_and_delete_all_cias = {"Install and delete all CIAs", COLOR_TEXT, action_install_cias_delete};
@ -38,25 +40,26 @@ static list_item install_and_delete_all_tickets = {"Install and delete all ticke
static list_item delete_all_tickets = {"Delete all tickets", COLOR_TEXT, action_delete_dir_tickets};
typedef struct {
Handle cancelEvent;
populate_files_data populateData;
bool populated;
FS_ArchiveID archiveId;
FS_Path archivePath;
FS_Archive archive;
void* archivePath;
file_info currDir;
file_info parentDir;
char currDir[FILE_PATH_MAX];
list_item* dirItem;
} files_data;
typedef struct {
linked_list* items;
list_item* selected;
file_info* target;
files_data* parent;
} files_action_data;
static void files_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
ui_draw_file_info(view, ((files_action_data*) data)->target, x1, y1, x2, y2);
ui_draw_file_info(view, ((files_action_data*) data)->selected->data, x1, y1, x2, y2);
}
static void files_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
@ -71,13 +74,24 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(linked_list*, list_item*, file_info*) = (void(*)(linked_list*, list_item*, file_info*)) selected->data;
if(selected != NULL && (selected->data != NULL || selected == &copy || selected == &copy_all_contents) && (selectedTouched || (hidKeysDown() & KEY_A))) {
void(*action)(linked_list*, list_item*) = (void(*)(linked_list*, list_item*)) selected->data;
ui_pop();
list_destroy(view);
action(actionData->items, actionData->selected, actionData->target);
if(selected == &copy || selected == &copy_all_contents) {
file_info* info = (file_info*) actionData->selected->data;
Result res = 0;
if(R_SUCCEEDED(res = clipboard_set_contents(actionData->parent->archiveId, &actionData->parent->archivePath, info->path, selected == &copy_all_contents))) {
prompt_display("Success", selected == &copy_all_contents ? "Current directory contents copied to clipboard." : info->isDirectory ? "Current directory copied to clipboard." : "File copied to clipboard.", COLOR_TEXT, false, info, NULL, ui_draw_file_info, NULL);
} else {
error_display_res(NULL, info, ui_draw_file_info, res, "Failed to copy to clipboard.");
}
} else {
action(actionData->items, actionData->selected);
}
free(data);
@ -85,14 +99,16 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l
}
if(linked_list_size(items) == 0) {
if(actionData->target->isDirectory) {
if(actionData->target->containsCias) {
file_info* info = (file_info*) actionData->selected->data;
if(info->isDirectory) {
if(info->containsCias) {
linked_list_add(items, &install_all_cias);
linked_list_add(items, &install_and_delete_all_cias);
linked_list_add(items, &delete_all_cias);
}
if(actionData->target->containsTickets) {
if(info->containsTickets) {
linked_list_add(items, &install_all_tickets);
linked_list_add(items, &install_and_delete_all_tickets);
linked_list_add(items, &delete_all_tickets);
@ -103,12 +119,12 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l
linked_list_add(items, &delete_dir);
} else {
if(actionData->target->isCia) {
if(info->isCia) {
linked_list_add(items, &install_cia);
linked_list_add(items, &install_and_delete_cia);
}
if(actionData->target->isTicket) {
if(info->isTicket) {
linked_list_add(items, &install_ticket);
linked_list_add(items, &install_and_delete_ticket);
}
@ -121,7 +137,7 @@ static void files_action_update(ui_view* view, void* data, linked_list* items, l
}
}
static void files_action_open(linked_list* items, list_item* selected, file_info* target) {
static void files_action_open(linked_list* items, list_item* selected, files_data* parent) {
files_action_data* data = (files_action_data*) calloc(1, sizeof(files_action_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate files action data.");
@ -131,10 +147,9 @@ static void files_action_open(linked_list* items, list_item* selected, file_info
data->items = items;
data->selected = selected;
data->parent = parent;
data->target = target;
list_display(target->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", data, files_action_update, files_action_draw_top);
list_display(((file_info*) selected->data)->isDirectory ? "Directory Action" : "File Action", "A: Select, B: Return", data, files_action_update, files_action_draw_top);
}
static void files_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
@ -144,91 +159,105 @@ static void files_draw_top(ui_view* view, void* data, float x1, float y1, float
}
static void files_repopulate(files_data* listData, linked_list* items) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
while(!util_is_dir(&listData->archive, listData->currDir.path)) {
char parentPath[FILE_PATH_MAX];
util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX);
strncpy(listData->currDir.path, parentPath, FILE_PATH_MAX);
util_get_path_file(listData->currDir.name, listData->currDir.path, FILE_NAME_MAX);
util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX);
strncpy(listData->parentDir.path, parentPath, FILE_PATH_MAX);
util_get_path_file(listData->parentDir.name, listData->parentDir.path, FILE_NAME_MAX);
if(listData->dirItem != NULL) {
task_free_file(listData->dirItem);
listData->dirItem = NULL;
}
Result res = 0;
if(R_SUCCEEDED(res = task_create_file_item(&listData->dirItem, listData->archive, listData->currDir))) {
listData->populateData.items = items;
listData->populateData.base = (file_info*) listData->dirItem->data;
res = task_populate_files(&listData->populateData);
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate file list population.");
}
listData->cancelEvent = task_populate_files(items, &listData->currDir);
listData->populated = true;
}
static void files_navigate(files_data* listData, linked_list* items, const char* path) {
strncpy(listData->currDir.path, path, FILE_PATH_MAX);
util_get_path_file(listData->currDir.name, listData->currDir.path, FILE_NAME_MAX);
strncpy(listData->currDir, path, FILE_PATH_MAX);
char parentPath[FILE_PATH_MAX];
util_get_parent_path(parentPath, listData->currDir.path, FILE_PATH_MAX);
strncpy(listData->parentDir.path, parentPath, FILE_PATH_MAX);
util_get_path_file(listData->parentDir.name, listData->parentDir.path, FILE_NAME_MAX);
listData->populated = false;
}
files_repopulate(listData, items);
static void files_free_data(files_data* data) {
if(!data->populateData.finished) {
svcSignalEvent(data->populateData.cancelEvent);
while(!data->populateData.finished) {
svcSleepThread(1000000);
}
}
if(data->dirItem != NULL) {
task_free_file(data->dirItem);
data->dirItem = NULL;
}
if(data->archive != 0) {
FSUSER_CloseArchive(data->archive);
data->archive = 0;
}
if(data->archivePath.data != NULL) {
free((void*) data->archivePath.data);
data->archivePath.data = NULL;
}
free(data);
}
static void files_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
files_data* listData = (files_data*) data;
while(!util_is_dir(listData->archive, listData->currDir)) {
char parentDir[FILE_PATH_MAX] = {'\0'};
util_get_parent_path(parentDir, listData->currDir, FILE_PATH_MAX);
files_navigate(listData, items, parentDir);
}
if(hidKeysDown() & KEY_B) {
if(strcmp(listData->currDir.path, "/") == 0) {
if(listData->archive.handle != 0) {
FSUSER_CloseArchive(&listData->archive);
listData->archive.handle = 0;
}
if(listData->archivePath != NULL) {
free(listData->archivePath);
listData->archivePath = NULL;
}
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
if(strncmp(listData->currDir, "/", FILE_PATH_MAX) == 0) {
ui_pop();
files_free_data(listData);
task_clear_files(items);
list_destroy(view);
free(listData);
return;
} else {
files_navigate(listData, items, listData->parentDir.path);
char parentDir[FILE_PATH_MAX] = {'\0'};
util_get_parent_path(parentDir, listData->currDir, FILE_PATH_MAX);
files_navigate(listData, items, parentDir);
}
}
if(hidKeysDown() & KEY_Y) {
files_action_open(items, selected, &listData->currDir);
if((hidKeysDown() & KEY_Y) && listData->dirItem != NULL) {
files_action_open(items, listData->dirItem, listData);
return;
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
file_info* fileInfo = (file_info*) selected->data;
if(util_is_dir(&listData->archive, fileInfo->path)) {
if(fileInfo->isDirectory) {
files_navigate(listData, items, fileInfo->path);
} else {
files_action_open(items, selected, fileInfo);
files_action_open(items, selected, listData);
return;
}
}
@ -236,9 +265,15 @@ static void files_update(ui_view* view, void* data, linked_list* items, list_ite
if(!listData->populated || (hidKeysDown() & KEY_X)) {
files_repopulate(listData, items);
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate file list.");
}
}
void files_open(FS_Archive archive) {
void files_open(FS_ArchiveID archiveId, FS_Path archivePath) {
files_data* data = (files_data*) calloc(1, sizeof(files_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate files data.");
@ -246,69 +281,61 @@ void files_open(FS_Archive archive) {
return;
}
data->archive = archive;
data->populateData.recursive = false;
data->populateData.includeBase = false;
data->populateData.dirsFirst = true;
if(data->archive.lowPath.size > 0) {
data->archivePath = calloc(1, data->archive.lowPath.size);
if(data->archivePath == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate files archive.");
data->populateData.finished = true;
free(data);
data->populated = false;
data->archiveId = archiveId;
data->archivePath.type = archivePath.type;
data->archivePath.size = archivePath.size;
if(archivePath.data != NULL) {
data->archivePath.data = calloc(1, data->archivePath.size);
if(data->archivePath.data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate files data.");
files_free_data(data);
return;
}
memcpy(data->archivePath, data->archive.lowPath.data, data->archive.lowPath.size);
data->archive.lowPath.data = data->archivePath;
memcpy((void*) data->archivePath.data, archivePath.data, data->archivePath.size);
} else {
data->archivePath.data = NULL;
}
data->archive.handle = 0;
snprintf(data->currDir, FILE_PATH_MAX, "/");
data->dirItem = NULL;
Result res = 0;
if(R_FAILED(res = FSUSER_OpenArchive(&data->archive))) {
if(R_FAILED(res = FSUSER_OpenArchive(&data->archive, archiveId, archivePath))) {
error_display_res(NULL, NULL, NULL, res, "Failed to open file listing archive.");
if(data->archivePath != NULL) {
free(data->archivePath);
}
free(data);
files_free_data(data);
return;
}
data->currDir.archive = &data->archive;
snprintf(data->currDir.path, FILE_PATH_MAX, "/");
util_get_path_file(data->currDir.name, data->currDir.path, FILE_NAME_MAX);
data->currDir.isDirectory = true;
data->currDir.containsCias = false;
data->currDir.size = 0;
data->currDir.isCia = false;
memcpy(&data->parentDir, &data->currDir, sizeof(data->parentDir));
list_display("Files", "A: Select, B: Back, X: Refresh, Y: Directory Action", data, files_update, files_draw_top);
}
void files_open_sd() {
FS_Archive sdmcArchive = {ARCHIVE_SDMC, {PATH_BINARY, 0, (void*) ""}};
files_open(sdmcArchive);
files_open(ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""));
}
void files_open_ctr_nand() {
FS_Archive ctrNandArchive = {ARCHIVE_NAND_CTR_FS, fsMakePath(PATH_EMPTY, "")};
files_open(ctrNandArchive);
files_open(ARCHIVE_NAND_CTR_FS, fsMakePath(PATH_EMPTY, ""));
}
void files_open_twl_nand() {
FS_Archive twlNandArchive = {ARCHIVE_NAND_TWL_FS, fsMakePath(PATH_EMPTY, "")};
files_open(twlNandArchive);
files_open(ARCHIVE_NAND_TWL_FS, fsMakePath(PATH_EMPTY, ""));
}
void files_open_twl_photo() {
FS_Archive twlPhotoArchive = {ARCHIVE_TWL_PHOTO, fsMakePath(PATH_EMPTY, "")};
files_open(twlPhotoArchive);
files_open(ARCHIVE_TWL_PHOTO, fsMakePath(PATH_EMPTY, ""));
}
void files_open_twl_sound() {
FS_Archive twlSoundArchive = {ARCHIVE_TWL_SOUND, {PATH_EMPTY, 0, ""}};
files_open(twlSoundArchive);
files_open(ARCHIVE_TWL_SOUND, fsMakePath(PATH_EMPTY, ""));
}

View File

@ -22,8 +22,7 @@ typedef struct {
u64 currTitleId;
bool ticket;
data_op_info installInfo;
Handle cancelEvent;
data_op_data installInfo;
} network_install_data;
static int recvwait(int sockfd, void* buf, size_t len, int flags) {
@ -174,8 +173,6 @@ static bool networkinstall_error(void* data, u32 index, Result res) {
prompt_display("Failure", "Install cancelled.", COLOR_TEXT, false, NULL, NULL, NULL, NULL);
} else if(res == R_FBI_ERRNO) {
error_display_errno(NULL, NULL, NULL, errno, "Failed to install over the network.");
} else if(res == R_FBI_WRONG_SYSTEM) {
error_display(NULL, NULL, NULL, "Failed to install over the network.\nAttempted to install N3DS title to O3DS.");
} else {
error_display_res(NULL, NULL, NULL, res, "Failed to install over the network.");
}
@ -190,7 +187,6 @@ static void networkinstall_close_client(network_install_data* data) {
close(data->clientSocket);
data->currTitleId = 0;
data->cancelEvent = 0;
}
static void networkinstall_install_update(ui_view* view, void* data, float* progress, char* text) {
@ -202,15 +198,15 @@ static void networkinstall_install_update(ui_view* view, void* data, float* prog
ui_pop();
info_destroy(view);
if(!networkInstallData->installInfo.premature) {
if(R_SUCCEEDED(networkInstallData->installInfo.result)) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, data, NULL, NULL, NULL);
}
return;
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(networkInstallData->cancelEvent);
if((hidKeysDown() & KEY_B) && !networkInstallData->installInfo.finished) {
svcSignalEvent(networkInstallData->installInfo.cancelEvent);
}
*progress = networkInstallData->installInfo.currTotal != 0 ? (float) ((double) networkInstallData->installInfo.currProcessed / (double) networkInstallData->installInfo.currTotal) : 0;
@ -221,11 +217,11 @@ static void networkinstall_confirm_onresponse(ui_view* view, void* data, bool re
network_install_data* networkInstallData = (network_install_data*) data;
if(response) {
networkInstallData->cancelEvent = task_data_op(&networkInstallData->installInfo);
if(networkInstallData->cancelEvent != 0) {
info_display("Installing Over Network", "Press B to cancel.", true, data, networkinstall_install_update, NULL);
Result res = task_data_op(&networkInstallData->installInfo);
if(R_SUCCEEDED(res)) {
info_display("Installing Received Files", "Press B to cancel.", true, data, networkinstall_install_update, NULL);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate installation.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate installation.");
networkinstall_close_client(networkInstallData);
}
@ -339,7 +335,5 @@ void networkinstall_open() {
data->installInfo.error = networkinstall_error;
data->cancelEvent = 0;
info_display("Network Install", "B: Return", false, data, networkinstall_wait_update, NULL);
}

View File

@ -16,7 +16,8 @@ static list_item delete_pending_title = {"Delete Pending Title", COLOR_TEXT, act
static list_item delete_all_pending_titles = {"Delete All Pending Titles", COLOR_TEXT, action_delete_all_pending_titles};
typedef struct {
Handle cancelEvent;
populate_pending_titles_data populateData;
bool populated;
} pendingtitles_data;
@ -84,13 +85,11 @@ static void pendingtitles_update(ui_view* view, void* data, linked_list* items,
pendingtitles_data* listData = (pendingtitles_data*) data;
if(hidKeysDown() & KEY_B) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
ui_pop();
@ -103,19 +102,28 @@ static void pendingtitles_update(ui_view* view, void* data, linked_list* items,
}
if(!listData->populated || (hidKeysDown() & KEY_X)) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
listData->cancelEvent = task_populate_pending_titles(items);
listData->populateData.items = items;
Result res = task_populate_pending_titles(&listData->populateData);
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate pending title list population.");
}
listData->populated = true;
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate pending title list.");
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
pendingtitles_action_open(items, selected);
return;
@ -130,5 +138,7 @@ void pendingtitles_open() {
return;
}
data->populateData.finished = true;
list_display("Pending Titles", "A: Select, B: Return, X: Refresh", data, pendingtitles_update, pendingtitles_draw_top);
}

View File

@ -23,17 +23,14 @@ typedef struct {
struct quirc* qrContext;
char urls[URLS_MAX][URL_MAX];
Handle mutex;
u16* buffer;
u32 tex;
Handle camCancelEvent;
u32 responseCode;
u64 currTitleId;
bool ticket;
data_op_info installInfo;
Handle installCancelEvent;
capture_cam_data captureInfo;
data_op_data installInfo;
} qr_install_data;
static Result qrinstall_is_src_directory(void* data, u32 index, bool* isDirectory) {
@ -182,13 +179,7 @@ static bool qrinstall_error(void* data, u32 index, Result res) {
char* url = qrInstallData->urls[index];
volatile bool dismissed = false;
if(res == R_FBI_WRONG_SYSTEM) {
if(strlen(url) > 48) {
error_display(&dismissed, NULL, NULL, "Failed to install from QR code.\n%.45s...\nAttempted to install N3DS title to O3DS.", url);
} else {
error_display(&dismissed, NULL, NULL, "Failed to install from QR code.\n%.48s\nAttempted to install N3DS title to O3DS.", url);
}
} else if(res == R_FBI_HTTP_RESPONSE_CODE) {
if(res == R_FBI_HTTP_RESPONSE_CODE) {
if(strlen(url) > 48) {
error_display(&dismissed, NULL, NULL, "Failed to install from QR code.\n%.45s...\nHTTP server returned response code %d", url, qrInstallData->responseCode);
} else {
@ -217,7 +208,7 @@ static void qrinstall_install_update(ui_view* view, void* data, float* progress,
ui_pop();
info_destroy(view);
if(!qrInstallData->installInfo.premature) {
if(R_SUCCEEDED(qrInstallData->installInfo.result)) {
prompt_display("Success", "Install finished.", COLOR_TEXT, false, data, NULL, NULL, NULL);
}
@ -225,7 +216,7 @@ static void qrinstall_install_update(ui_view* view, void* data, float* progress,
}
if(hidKeysDown() & KEY_B) {
svcSignalEvent(qrInstallData->installCancelEvent);
svcSignalEvent(qrInstallData->installInfo.cancelEvent);
}
*progress = qrInstallData->installInfo.currTotal != 0 ? (float) ((double) qrInstallData->installInfo.currProcessed / (double) qrInstallData->installInfo.currTotal) : 0;
@ -236,33 +227,26 @@ static void qrinstall_confirm_onresponse(ui_view* view, void* data, bool respons
qr_install_data* qrInstallData = (qr_install_data*) data;
if(response) {
qrInstallData->installCancelEvent = task_data_op(&qrInstallData->installInfo);
if(qrInstallData->installCancelEvent != 0) {
Result res = task_data_op(&qrInstallData->installInfo);
if(R_SUCCEEDED(res)) {
info_display("Installing From QR Code", "Press B to cancel.", true, data, qrinstall_install_update, NULL);
} else {
error_display(NULL, NULL, NULL, "Failed to initiate installation.");
error_display_res(NULL, NULL, NULL, res, "Failed to initiate installation.");
}
}
}
static void qrinstall_free_data(qr_install_data* data) {
if(data->camCancelEvent != 0) {
svcSignalEvent(data->camCancelEvent);
while(svcWaitSynchronization(data->camCancelEvent, 0) == 0) {
if(!data->installInfo.finished) {
svcSignalEvent(data->installInfo.cancelEvent);
while(!data->installInfo.finished) {
svcSleepThread(1000000);
}
data->camCancelEvent = 0;
}
if(data->qrContext != NULL) {
quirc_destroy(data->qrContext);
data->qrContext = NULL;
}
if(data->buffer != NULL) {
free(data->buffer);
data->buffer = NULL;
if(data->captureInfo.buffer != NULL) {
free(data->captureInfo.buffer);
data->captureInfo.buffer = NULL;
}
if(data->tex != 0) {
@ -270,6 +254,11 @@ static void qrinstall_free_data(qr_install_data* data) {
data->tex = 0;
}
if(data->qrContext != NULL) {
quirc_destroy(data->qrContext);
data->qrContext = NULL;
}
free(data);
}
@ -293,6 +282,19 @@ static void qrinstall_wait_update(ui_view* view, void* data, float* progress, ch
return;
}
if(qrInstallData->captureInfo.finished) {
ui_pop();
info_destroy(view);
if(R_FAILED(qrInstallData->captureInfo.result)) {
error_display_res(NULL, NULL, NULL, qrInstallData->captureInfo.result, "Error while capturing camera frames.");
}
qrinstall_free_data(qrInstallData);
return;
}
if(qrInstallData->tex != 0) {
screen_unload_texture(qrInstallData->tex);
qrInstallData->tex = 0;
@ -302,18 +304,18 @@ static void qrinstall_wait_update(ui_view* view, void* data, float* progress, ch
int h = 0;
uint8_t* qrBuf = quirc_begin(qrInstallData->qrContext, &w, &h);
svcWaitSynchronization(qrInstallData->mutex, U64_MAX);
svcWaitSynchronization(qrInstallData->captureInfo.mutex, U64_MAX);
qrInstallData->tex = screen_load_texture_auto(qrInstallData->buffer, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16), IMAGE_WIDTH, IMAGE_HEIGHT, GPU_RGB565, false);
qrInstallData->tex = screen_load_texture_auto(qrInstallData->captureInfo.buffer, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16), IMAGE_WIDTH, IMAGE_HEIGHT, GPU_RGB565, false);
for(int x = 0; x < w; x++) {
for(int y = 0; y < h; y++) {
u16 px = qrInstallData->buffer[y * IMAGE_WIDTH + x];
u16 px = qrInstallData->captureInfo.buffer[y * IMAGE_WIDTH + x];
qrBuf[y * w + x] = (u8) (((((px >> 11) & 0x1F) << 3) + (((px >> 5) & 0x3F) << 2) + ((px & 0x1F) << 3)) / 3);
}
}
svcReleaseMutex(qrInstallData->mutex);
svcReleaseMutex(qrInstallData->captureInfo.mutex);
quirc_end(qrInstallData->qrContext);
@ -375,17 +377,19 @@ void qrinstall_open() {
return;
}
data->buffer = (u16*) calloc(1, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16));
if(data->buffer == NULL) {
data->captureInfo.width = IMAGE_WIDTH;
data->captureInfo.height = IMAGE_HEIGHT;
data->captureInfo.buffer = (u16*) calloc(1, IMAGE_WIDTH * IMAGE_HEIGHT * sizeof(u16));
if(data->captureInfo.buffer == NULL) {
error_display(NULL, NULL, NULL, "Failed to create image buffer.");
qrinstall_free_data(data);
return;
}
data->camCancelEvent = task_capture_cam(&data->mutex, data->buffer, IMAGE_WIDTH, IMAGE_HEIGHT);
if(data->camCancelEvent == 0) {
error_display(NULL, NULL, NULL, "Failed to start camera capture.");
Result capRes = task_capture_cam(&data->captureInfo);
if(R_FAILED(capRes)) {
error_display_res(NULL, NULL, NULL, capRes, "Failed to start camera capture.");
qrinstall_free_data(data);
return;
@ -419,7 +423,5 @@ void qrinstall_open() {
data->installInfo.error = qrinstall_error;
data->installCancelEvent = 0;
info_display("QR Code Install", "B: Return", false, data, qrinstall_wait_update, qrinstall_wait_draw_top);
}

View File

@ -2,7 +2,7 @@
void dumpnand_open();
void extsavedata_open();
void files_open(FS_Archive archive);
void files_open(FS_ArchiveID archiveId, FS_Path archivePath);
void files_open_sd();
void files_open_ctr_nand();
void files_open_twl_nand();

View File

@ -16,7 +16,8 @@ static list_item browse_save_data = {"Browse Save Data", COLOR_TEXT, action_brow
static list_item delete_save_data = {"Delete Save Data", COLOR_TEXT, action_delete_system_save_data};
typedef struct {
Handle cancelEvent;
populate_system_save_data_data populateData;
bool populated;
} systemsavedata_data;
@ -84,13 +85,11 @@ static void systemsavedata_update(ui_view* view, void* data, linked_list* items,
systemsavedata_data* listData = (systemsavedata_data*) data;
if(hidKeysDown() & KEY_B) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
ui_pop();
@ -103,19 +102,28 @@ static void systemsavedata_update(ui_view* view, void* data, linked_list* items,
}
if(!listData->populated || (hidKeysDown() & KEY_X)) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
listData->cancelEvent = task_populate_system_save_data(items);
listData->populateData.items = items;
Result res = task_populate_system_save_data(&listData->populateData);
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate system save data list population.");
}
listData->populated = true;
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate system save data list.");
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
systemsavedata_action_open(items, selected);
return;
@ -130,5 +138,7 @@ void systemsavedata_open() {
return;
}
data->populateData.finished = true;
list_display("System Save Data", "A: Select, B: Return, X: Refresh", data, systemsavedata_update, systemsavedata_draw_top);
}

View File

@ -13,16 +13,6 @@
#define EVENT_COUNT 3
typedef struct {
u16* buffer;
s16 width;
s16 height;
Handle mutex;
Handle cancelEvent;
} capture_cam_data;
static void task_capture_cam_thread(void* arg) {
capture_cam_data* data = (capture_cam_data*) arg;
@ -109,10 +99,6 @@ static void task_capture_cam_thread(void* arg) {
res = R_FBI_OUT_OF_MEMORY;
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Error capturing camera image.");
}
for(int i = 0; i < EVENT_COUNT; i++) {
if(events[i] != 0) {
svcCloseHandle(events[i]);
@ -121,51 +107,41 @@ static void task_capture_cam_thread(void* arg) {
}
svcCloseHandle(data->mutex);
free(data);
data->result = res;
data->finished = true;
}
Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height) {
if(buffer == NULL || width <= 0 || width > 640 || height <= 0 || height > 480 || mutex == 0) {
return 0;
Result task_capture_cam(capture_cam_data* data) {
if(data == NULL || data->buffer == NULL || data->width <= 0 || data->width > 640 || data->height <= 0 || data->height > 480 || data->mutex == 0) {
return R_FBI_INVALID_ARGUMENT;
}
capture_cam_data* data = (capture_cam_data*) calloc(1, sizeof(capture_cam_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate camera capture data.");
return 0;
data->mutex = 0;
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1)) && R_SUCCEEDED(res = svcCreateMutex(&data->mutex, false))) {
if(threadCreate(task_capture_cam_thread, data, 0x10000, 0x19, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->buffer = buffer;
data->width = width;
data->height = height;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create camera capture cancel event.");
free(data);
return 0;
if(data->mutex != 0) {
svcCloseHandle(data->mutex);
data->mutex = 0;
}
}
Result mutexRes = svcCreateMutex(&data->mutex, false);
if(R_FAILED(mutexRes)) {
error_display_res(NULL, NULL, NULL, mutexRes, "Failed to create camera capture buffer mutex.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
if(threadCreate(task_capture_cam_thread, data, 0x10000, 0x19, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create camera capture thread.");
svcCloseHandle(data->mutex);
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
*mutex = data->mutex;
return data->cancelEvent;
return res;
}

View File

@ -7,30 +7,24 @@
#include "../../list.h"
#include "../../error.h"
typedef struct {
data_op_info* info;
Handle cancelEvent;
} data_op_data;
static bool task_data_op_copy(data_op_data* data, u32 index) {
data->info->currProcessed = 0;
data->info->currTotal = 0;
data->currProcessed = 0;
data->currTotal = 0;
Result res = 0;
bool isDir = false;
if(R_SUCCEEDED(res = data->info->isSrcDirectory(data->info->data, index, &isDir)) && isDir) {
res = data->info->makeDstDirectory(data->info->data, index);
if(R_SUCCEEDED(res = data->isSrcDirectory(data->data, index, &isDir)) && isDir) {
res = data->makeDstDirectory(data->data, index);
} else {
u32 srcHandle = 0;
if(R_SUCCEEDED(res = data->info->openSrc(data->info->data, index, &srcHandle))) {
if(R_SUCCEEDED(res = data->info->getSrcSize(data->info->data, srcHandle, &data->info->currTotal))) {
if(data->info->currTotal == 0) {
if(data->info->copyEmpty) {
if(R_SUCCEEDED(res = data->openSrc(data->data, index, &srcHandle))) {
if(R_SUCCEEDED(res = data->getSrcSize(data->data, srcHandle, &data->currTotal))) {
if(data->currTotal == 0) {
if(data->copyEmpty) {
u32 dstHandle = 0;
if(R_SUCCEEDED(res = data->info->openDst(data->info->data, index, NULL, &dstHandle))) {
res = data->info->closeDst(data->info->data, index, true, dstHandle);
if(R_SUCCEEDED(res = data->openDst(data->data, index, NULL, &dstHandle))) {
res = data->closeDst(data->data, index, true, dstHandle);
}
}
} else {
@ -40,7 +34,7 @@ static bool task_data_op_copy(data_op_data* data, u32 index) {
u32 dstHandle = 0;
bool firstRun = true;
while(data->info->currProcessed < data->info->currTotal) {
while(data->currProcessed < data->currTotal) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
res = R_FBI_CANCELLED;
@ -48,33 +42,33 @@ static bool task_data_op_copy(data_op_data* data, u32 index) {
}
u32 currSize = bufferSize;
if((u64) currSize > data->info->currTotal - data->info->currProcessed) {
currSize = (u32) (data->info->currTotal - data->info->currProcessed);
if((u64) currSize > data->currTotal - data->currProcessed) {
currSize = (u32) (data->currTotal - data->currProcessed);
}
u32 bytesRead = 0;
u32 bytesWritten = 0;
if(R_FAILED(res = data->info->readSrc(data->info->data, srcHandle, &bytesRead, buffer, data->info->currProcessed, currSize))) {
if(R_FAILED(res = data->readSrc(data->data, srcHandle, &bytesRead, buffer, data->currProcessed, currSize))) {
break;
}
if(firstRun) {
firstRun = false;
if(R_FAILED(res = data->info->openDst(data->info->data, index, buffer, &dstHandle))) {
if(R_FAILED(res = data->openDst(data->data, index, buffer, &dstHandle))) {
break;
}
}
if(R_FAILED(res = data->info->writeDst(data->info->data, dstHandle, &bytesWritten, buffer, data->info->currProcessed, currSize))) {
if(R_FAILED(res = data->writeDst(data->data, dstHandle, &bytesWritten, buffer, data->currProcessed, currSize))) {
break;
}
data->info->currProcessed += bytesWritten;
data->currProcessed += bytesWritten;
}
if(dstHandle != 0) {
Result closeDstRes = data->info->closeDst(data->info->data, index, res == 0, dstHandle);
Result closeDstRes = data->closeDst(data->data, index, res == 0, dstHandle);
if(R_SUCCEEDED(res)) {
res = closeDstRes;
}
@ -87,7 +81,7 @@ static bool task_data_op_copy(data_op_data* data, u32 index) {
}
}
Result closeSrcRes = data->info->closeSrc(data->info->data, index, res == 0, srcHandle);
Result closeSrcRes = data->closeSrc(data->data, index, res == 0, srcHandle);
if(R_SUCCEEDED(res)) {
res = closeSrcRes;
}
@ -95,7 +89,8 @@ static bool task_data_op_copy(data_op_data* data, u32 index) {
}
if(R_FAILED(res)) {
return data->info->error(data->info->data, index, res);
data->result = res;
return data->error(data->data, index, res);
}
return true;
@ -103,8 +98,8 @@ static bool task_data_op_copy(data_op_data* data, u32 index) {
static bool task_data_op_delete(data_op_data* data, u32 index) {
Result res = 0;
if(R_FAILED(res = data->info->delete(data->info->data, index))) {
return data->info->error(data->info->data, index, res);
if(R_FAILED(res = data->delete(data->data, index))) {
return data->error(data->data, index, res);
}
return true;
@ -113,78 +108,57 @@ static bool task_data_op_delete(data_op_data* data, u32 index) {
static void task_data_op_thread(void* arg) {
data_op_data* data = (data_op_data*) arg;
data->info->finished = false;
data->info->premature = false;
data->info->processed = 0;
for(data->info->processed = 0; data->info->processed < data->info->total; data->info->processed++) {
for(data->processed = 0; data->processed < data->total; data->processed++) {
bool cont = false;
switch(data->info->op) {
switch(data->op) {
case DATAOP_COPY:
cont = task_data_op_copy(data, data->info->processed);
cont = task_data_op_copy(data, data->processed);
break;
case DATAOP_DELETE:
cont = task_data_op_delete(data, data->info->processed);
cont = task_data_op_delete(data, data->processed);
break;
default:
break;
}
if(!cont) {
data->info->premature = true;
break;
}
}
data->info->finished = true;
svcCloseHandle(data->cancelEvent);
free(data);
data->finished = true;
}
static void task_data_op_reset_info(data_op_info* info) {
info->finished = false;
info->premature = false;
info->processed = 0;
info->currProcessed = 0;
info->currTotal = 0;
}
Handle task_data_op(data_op_info* info) {
if(info == NULL) {
return 0;
}
task_data_op_reset_info(info);
data_op_data* data = (data_op_data*) calloc(1, sizeof(data_op_data));
Result task_data_op(data_op_data* data) {
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate data operation data.");
return 0;
return R_FBI_INVALID_ARGUMENT;
}
data->info = info;
data->processed = 0;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create data operation cancel event.");
data->currProcessed = 0;
data->currTotal = 0;
free(data);
return 0;
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
if(threadCreate(task_data_op_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create data operation thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
return data->cancelEvent;
return res;
}

View File

@ -14,11 +14,12 @@
#define MAX_EXT_SAVE_DATA 512
typedef struct {
linked_list* items;
static int task_populate_ext_save_data_compare_ids(const void* e1, const void* e2) {
u64 id1 = *(u64*) e1;
u64 id2 = *(u64*) e2;
Handle cancelEvent;
} populate_ext_save_data_data;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data, FS_MediaType mediaType) {
Result res = 0;
@ -26,9 +27,8 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
u32 extSaveDataCount = 0;
u64 extSaveDataIds[MAX_EXT_SAVE_DATA];
if(R_SUCCEEDED(res = FSUSER_EnumerateExtSaveData(&extSaveDataCount, MAX_EXT_SAVE_DATA, mediaType, 8, mediaType == MEDIATYPE_NAND, (u8*) extSaveDataIds))) {
qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), util_compare_u64);
qsort(extSaveDataIds, extSaveDataCount, sizeof(u64), task_populate_ext_save_data_compare_ids);
SMDH smdh;
for(u32 i = 0; i < extSaveDataCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
@ -45,18 +45,26 @@ static Result task_populate_ext_save_data_from(populate_ext_save_data_data* data
extSaveDataInfo->hasMeta = false;
FS_ExtSaveDataInfo info = {.mediaType = mediaType, .saveId = extSaveDataIds[i]};
u32 smdhBytesRead = 0;
if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) &smdh)) && smdhBytesRead == sizeof(SMDH)) {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, LIST_ITEM_NAME_MAX - 1);
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
if(smdh != NULL) {
u32 smdhBytesRead = 0;
if(R_SUCCEEDED(FSUSER_ReadExtSaveDataIcon(&smdhBytesRead, info, sizeof(SMDH), (u8*) smdh)) && smdhBytesRead == sizeof(SMDH)) {
if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
extSaveDataInfo->hasMeta = true;
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(extSaveDataInfo->meta.publisher) - 1);
extSaveDataInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, LIST_ITEM_NAME_MAX - 1);
extSaveDataInfo->hasMeta = true;
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(extSaveDataInfo->meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(extSaveDataInfo->meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) extSaveDataInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(extSaveDataInfo->meta.publisher) - 1);
extSaveDataInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
}
}
free(smdh);
}
bool empty = strlen(item->name) == 0;
@ -105,12 +113,15 @@ static void task_populate_ext_save_data_thread(void* arg) {
populate_ext_save_data_data* data = (populate_ext_save_data_data*) arg;
Result res = 0;
if(R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_ext_save_data_from(data, MEDIATYPE_NAND))) {
error_display_res(NULL, NULL, NULL, res, "Failed to load ext save data listing.");
if(R_SUCCEEDED(res = task_populate_ext_save_data_from(data, MEDIATYPE_SD))) {
res = task_populate_ext_save_data_from(data, MEDIATYPE_NAND);
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_ext_save_data(list_item* item) {
@ -145,37 +156,30 @@ void task_clear_ext_save_data(linked_list* items) {
}
}
Handle task_populate_ext_save_data(linked_list* items) {
if(items == NULL) {
return 0;
Result task_populate_ext_save_data(populate_ext_save_data_data* data) {
if(data == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_ext_save_data(items);
task_clear_ext_save_data(data->items);
populate_ext_save_data_data* data = (populate_ext_save_data_data*) calloc(1, sizeof(populate_ext_save_data_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate ext save data list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create ext save data list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_ext_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create ext save data list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -14,14 +14,7 @@
#define MAX_FILES 1024
typedef struct {
linked_list* items;
file_info* dir;
Handle cancelEvent;
} populate_files_data;
Result task_create_file_item(list_item** out, FS_Archive* archive, const char* path) {
Result task_create_file_item(list_item** out, FS_Archive archive, const char* path) {
Result res = 0;
list_item* item = (list_item*) calloc(1, sizeof(list_item));
@ -30,7 +23,6 @@ Result task_create_file_item(list_item** out, FS_Archive* archive, const char* p
if(fileInfo != NULL) {
fileInfo->archive = archive;
util_get_path_file(fileInfo->name, path, FILE_NAME_MAX);
fileInfo->containsCias = false;
fileInfo->size = 0;
fileInfo->isCia = false;
@ -46,7 +38,7 @@ Result task_create_file_item(list_item** out, FS_Archive* archive, const char* p
fileInfo->isDirectory = true;
} else {
item->color = COLOR_TEXT;
item->color = COLOR_FILE;
strncpy(fileInfo->path, path, FILE_PATH_MAX);
fileInfo->isDirectory = false;
@ -54,7 +46,7 @@ Result task_create_file_item(list_item** out, FS_Archive* archive, const char* p
FS_Path* fileFsPath = util_make_path_utf8(fileInfo->path);
if(fileFsPath != NULL) {
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, *archive, *fileFsPath, FS_OPEN_READ, 0))) {
if(R_SUCCEEDED(FSUSER_OpenFile(&fileHandle, archive, *fileFsPath, FS_OPEN_READ, 0))) {
FSFILE_GetSize(fileHandle, &fileInfo->size);
size_t len = strlen(fileInfo->path);
@ -72,16 +64,22 @@ Result task_create_file_item(list_item** out, FS_Archive* archive, const char* p
fileInfo->ciaInfo.installedSize = titleEntry.size;
}
SMDH smdh;
if(R_SUCCEEDED(AM_GetCiaIcon(&smdh, fileHandle))) {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
if(smdh != NULL) {
if(R_SUCCEEDED(AM_GetCiaIcon(smdh, fileHandle))) {
if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') {
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
fileInfo->ciaInfo.hasMeta = true;
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.meta.publisher) - 1);
fileInfo->ciaInfo.meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
fileInfo->ciaInfo.hasMeta = true;
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(fileInfo->ciaInfo.meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(fileInfo->ciaInfo.meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) fileInfo->ciaInfo.meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(fileInfo->ciaInfo.meta.publisher) - 1);
fileInfo->ciaInfo.meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
}
}
free(smdh);
}
}
} else if(strcasecmp(&fileInfo->path[len - 4], ".tik") == 0) {
@ -124,72 +122,128 @@ Result task_create_file_item(list_item** out, FS_Archive* archive, const char* p
return res;
}
static int task_populate_files_compare_directory_entries(const void* e1, const void* e2) {
FS_DirectoryEntry* ent1 = (FS_DirectoryEntry*) e1;
FS_DirectoryEntry* ent2 = (FS_DirectoryEntry*) e2;
if((ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && !(ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) {
return -1;
} else if(!(ent1->attributes & FS_ATTRIBUTE_DIRECTORY) && (ent2->attributes & FS_ATTRIBUTE_DIRECTORY)) {
return 1;
} else {
char entryName1[0x213] = {'\0'};
utf16_to_utf8((uint8_t*) entryName1, ent1->name, sizeof(entryName1) - 1);
char entryName2[0x213] = {'\0'};
utf16_to_utf8((uint8_t*) entryName2, ent2->name, sizeof(entryName2) - 1);
return strcasecmp(entryName1, entryName2);
}
}
static void task_populate_files_thread(void* arg) {
populate_files_data* data = (populate_files_data*) arg;
data->dir->containsCias = false;
data->dir->containsTickets = false;
Result res = 0;
FS_Path* fsPath = util_make_path_utf8(data->dir->path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, *data->dir->archive, *fsPath))) {
u32 entryCount = 0;
FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry));
if(entries != NULL) {
if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries)) && entryCount > 0) {
qsort(entries, entryCount, sizeof(FS_DirectoryEntry), util_compare_directory_entries);
data->base->containsCias = false;
data->base->containsTickets = false;
for(u32 i = 0; i < entryCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
break;
}
list_item* baseItem = NULL;
if(R_SUCCEEDED(res = task_create_file_item(&baseItem, data->base->archive, data->base->path))) {
linked_list queue;
linked_list_init(&queue);
if(entries[i].attributes & FS_ATTRIBUTE_HIDDEN) {
continue;
}
linked_list_add(&queue, baseItem);
char name[FILE_NAME_MAX] = {'\0'};
utf16_to_utf8((uint8_t*) name, entries[i].name, FILE_NAME_MAX - 1);
bool quit = false;
while(!quit && R_SUCCEEDED(res) && linked_list_size(&queue) > 0) {
u32 tail = linked_list_size(&queue) - 1;
list_item* currItem = (list_item*) linked_list_get(&queue, tail);
file_info* curr = (file_info*) currItem->data;
linked_list_remove_at(&queue, tail);
char path[FILE_PATH_MAX] = {'\0'};
snprintf(path, FILE_PATH_MAX, "%s%s", data->dir->path, name);
list_item* item = NULL;
if(R_SUCCEEDED(res = task_create_file_item(&item, data->dir->archive, path))) {
if(((file_info*) item->data)->isCia) {
data->dir->containsCias = true;
} else if(((file_info*) item->data)->isTicket) {
data->dir->containsTickets = true;
}
linked_list_add(data->items, item);
}
}
}
free(entries);
} else {
res = R_FBI_OUT_OF_MEMORY;
if(data->dirsFirst && (data->includeBase || currItem != baseItem)) {
linked_list_add(data->items, currItem);
}
FSDIR_Close(dirHandle);
if(curr->isDirectory) {
FS_Path* fsPath = util_make_path_utf8(curr->path);
if(fsPath != NULL) {
Handle dirHandle = 0;
if(R_SUCCEEDED(res = FSUSER_OpenDirectory(&dirHandle, curr->archive, *fsPath))) {
u32 entryCount = 0;
FS_DirectoryEntry* entries = (FS_DirectoryEntry*) calloc(MAX_FILES, sizeof(FS_DirectoryEntry));
if(entries != NULL) {
if(R_SUCCEEDED(res = FSDIR_Read(dirHandle, &entryCount, MAX_FILES, entries)) && entryCount > 0) {
qsort(entries, entryCount, sizeof(FS_DirectoryEntry), task_populate_files_compare_directory_entries);
for(u32 i = 0; i < entryCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
quit = true;
break;
}
if(entries[i].attributes & FS_ATTRIBUTE_HIDDEN) {
continue;
}
char name[FILE_NAME_MAX] = {'\0'};
utf16_to_utf8((uint8_t*) name, entries[i].name, FILE_NAME_MAX - 1);
char path[FILE_PATH_MAX] = {'\0'};
snprintf(path, FILE_PATH_MAX, "%s%s", curr->path, name);
list_item* item = NULL;
if(R_SUCCEEDED(res = task_create_file_item(&item, curr->archive, path))) {
if(curr->isDirectory && strncmp(curr->path, data->base->path, FILE_PATH_MAX) == 0) {
file_info* info = (file_info*) item->data;
if(info->isCia) {
data->base->containsCias = true;
} else if(info->isTicket) {
data->base->containsTickets = true;
}
}
if(data->recursive && ((file_info*) item->data)->isDirectory) {
linked_list_add(&queue, item);
} else {
linked_list_add(data->items, item);
}
}
}
}
free(entries);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
FSDIR_Close(dirHandle);
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
}
if(!data->dirsFirst && (data->includeBase || currItem != baseItem)) {
linked_list_add(data->items, currItem);
}
}
util_free_path_utf8(fsPath);
} else {
res = R_FBI_OUT_OF_MEMORY;
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to load file listing.");
if(!data->includeBase) {
task_free_file(baseItem);
}
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_file(list_item* item) {
@ -219,43 +273,36 @@ void task_clear_files(linked_list* items) {
while(linked_list_iter_has_next(&iter)) {
list_item* item = (list_item*) linked_list_iter_next(&iter);
task_free_file(item);
linked_list_iter_remove(&iter);
task_free_file(item);
}
}
Handle task_populate_files(linked_list* items, file_info* dir) {
if(items == NULL || dir == NULL) {
return 0;
Result task_populate_files(populate_files_data* data) {
if(data == NULL || data->base == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_files(items);
task_clear_files(data->items);
populate_files_data* data = (populate_files_data*) calloc(1, sizeof(populate_files_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate file list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_files_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
data->dir = dir;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create file list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_files_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create file list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -11,11 +11,12 @@
#include "../../../core/screen.h"
#include "../../../core/util.h"
typedef struct {
linked_list* items;
static int task_populate_pending_titles_compare_ids(const void* e1, const void* e2) {
u64 id1 = *(u64*) e1;
u64 id2 = *(u64*) e2;
Handle cancelEvent;
} populate_pending_titles_data;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
static Result task_populate_pending_titles_from(populate_pending_titles_data* data, FS_MediaType mediaType) {
Result res = 0;
@ -25,7 +26,7 @@ static Result task_populate_pending_titles_from(populate_pending_titles_data* da
u64* pendingTitleIds = (u64*) calloc(pendingTitleCount, sizeof(u64));
if(pendingTitleIds != NULL) {
if(R_SUCCEEDED(res = AM_GetPendingTitleList(&pendingTitleCount, pendingTitleCount, mediaType, AM_STATUS_MASK_INSTALLING | AM_STATUS_MASK_AWAITING_FINALIZATION, pendingTitleIds))) {
qsort(pendingTitleIds, pendingTitleCount, sizeof(u64), util_compare_u64);
qsort(pendingTitleIds, pendingTitleCount, sizeof(u64), task_populate_pending_titles_compare_ids);
AM_PendingTitleEntry* pendingTitleInfos = (AM_PendingTitleEntry*) calloc(pendingTitleCount, sizeof(AM_PendingTitleEntry));
if(pendingTitleInfos != NULL) {
@ -84,12 +85,15 @@ static void task_populate_pending_titles_thread(void* arg) {
populate_pending_titles_data* data = (populate_pending_titles_data*) arg;
Result res = 0;
if(R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_SD)) || R_FAILED(res = task_populate_pending_titles_from(data, MEDIATYPE_NAND))) {
error_display_res(NULL, NULL, NULL, res, "Failed to load pending title listing.");
if(R_SUCCEEDED(res = task_populate_pending_titles_from(data, MEDIATYPE_SD))) {
res = task_populate_pending_titles_from(data, MEDIATYPE_NAND);
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_pending_title(list_item* item) {
@ -119,37 +123,30 @@ void task_clear_pending_titles(linked_list* items) {
}
}
Handle task_populate_pending_titles(linked_list* items) {
if(items == NULL) {
return 0;
Result task_populate_pending_titles(populate_pending_titles_data* data) {
if(data == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_pending_titles(items);
task_clear_pending_titles(data->items);
populate_pending_titles_data* data = (populate_pending_titles_data*) calloc(1, sizeof(populate_pending_titles_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate pending title list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create pending title list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_pending_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create pending title list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -13,11 +13,12 @@
#define MAX_SYSTEM_SAVE_DATA 512
typedef struct {
linked_list* items;
static int task_populate_system_save_data_compare_ids(const void* e1, const void* e2) {
u32 id1 = *(u32*) e1;
u32 id2 = *(u32*) e2;
Handle cancelEvent;
} populate_system_save_data_data;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
static void task_populate_system_save_data_thread(void* arg) {
populate_system_save_data_data* data = (populate_system_save_data_data*) arg;
@ -27,7 +28,7 @@ static void task_populate_system_save_data_thread(void* arg) {
u32 systemSaveDataCount = 0;
u32 systemSaveDataIds[MAX_SYSTEM_SAVE_DATA];
if(R_SUCCEEDED(res = FSUSER_EnumerateSystemSaveData(&systemSaveDataCount, MAX_SYSTEM_SAVE_DATA * sizeof(u32), systemSaveDataIds))) {
qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), util_compare_u32);
qsort(systemSaveDataIds, systemSaveDataCount, sizeof(u32), task_populate_system_save_data_compare_ids);
for(u32 i = 0; i < systemSaveDataCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
@ -57,12 +58,10 @@ static void task_populate_system_save_data_thread(void* arg) {
}
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to load system save data listing.");
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_system_save_data(list_item* item) {
@ -92,37 +91,30 @@ void task_clear_system_save_data(linked_list* items) {
}
}
Handle task_populate_system_save_data(linked_list* items) {
if(items == NULL) {
return 0;
Result task_populate_system_save_data(populate_system_save_data_data* data) {
if(data == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_system_save_data(items);
task_clear_system_save_data(data->items);
populate_system_save_data_data* data = (populate_system_save_data_data*) calloc(1, sizeof(populate_system_save_data_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate system save data list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create system save data list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_system_save_data_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create system save data list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -11,11 +11,12 @@
#include "../../../core/screen.h"
#include "../../../core/util.h"
typedef struct {
linked_list* items;
static int task_populate_tickets_compare_ids(const void* e1, const void* e2) {
u64 id1 = *(u64*) e1;
u64 id2 = *(u64*) e2;
Handle cancelEvent;
} populate_tickets_data;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
static void task_populate_tickets_thread(void* arg) {
populate_tickets_data* data = (populate_tickets_data*) arg;
@ -27,7 +28,7 @@ static void task_populate_tickets_thread(void* arg) {
u64* ticketIds = (u64*) calloc(ticketCount, sizeof(u64));
if(ticketIds != NULL) {
if(R_SUCCEEDED(res = AM_GetTicketList(&ticketCount, ticketCount, 0, ticketIds))) {
qsort(ticketIds, ticketCount, sizeof(u64), util_compare_u64);
qsort(ticketIds, ticketCount, sizeof(u64), task_populate_tickets_compare_ids);
for(u32 i = 0; i < ticketCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
@ -63,12 +64,10 @@ static void task_populate_tickets_thread(void* arg) {
}
}
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to load ticket listing.");
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_ticket(list_item* item) {
@ -98,37 +97,30 @@ void task_clear_tickets(linked_list* items) {
}
}
Handle task_populate_tickets(linked_list* items) {
if(items == NULL) {
return 0;
Result task_populate_tickets(populate_tickets_data* data) {
if(data == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_tickets(items);
task_clear_tickets(data->items);
populate_tickets_data* data = (populate_tickets_data*) calloc(1, sizeof(populate_tickets_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate ticket list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create ticket list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_tickets_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create ticket list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -13,12 +13,6 @@
#include "../../../core/screen.h"
#include "../../../core/util.h"
typedef struct {
linked_list* items;
Handle cancelEvent;
} populate_titles_data;
static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaType mediaType, u64 titleId) {
Result res = 0;
@ -36,30 +30,31 @@ static Result task_populate_titles_add_ctr(populate_titles_data* data, FS_MediaT
titleInfo->twl = false;
titleInfo->hasMeta = false;
static const u32 filePathData[] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
static const FS_Path filePath = (FS_Path) {PATH_BINARY, 0x14, (u8*) filePathData};
u32 archivePath[] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
FS_Archive archive = {ARCHIVE_SAVEDATA_AND_CONTENT, (FS_Path) {PATH_BINARY, 0x10, (u8*) archivePath}};
static const u32 filePath[5] = {0x00000000, 0x00000000, 0x00000002, 0x6E6F6369, 0x00000000};
u32 archivePath[4] = {(u32) (titleId & 0xFFFFFFFF), (u32) ((titleId >> 32) & 0xFFFFFFFF), mediaType, 0x00000000};
Handle fileHandle;
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, archive, filePath, FS_OPEN_READ, 0))) {
SMDH smdh;
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&fileHandle, ARCHIVE_SAVEDATA_AND_CONTENT, util_make_binary_path(archivePath, sizeof(archivePath)), util_make_binary_path(filePath, sizeof(filePath)), FS_OPEN_READ, 0))) {
SMDH* smdh = (SMDH*) calloc(1, sizeof(SMDH));
if(smdh != NULL) {
u32 bytesRead = 0;
if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
if(smdh->magic[0] == 'S' && smdh->magic[1] == 'M' && smdh->magic[2] == 'D' && smdh->magic[3] == 'H') {
titleInfo->hasMeta = true;
u32 bytesRead = 0;
if(R_SUCCEEDED(FSFILE_Read(fileHandle, &bytesRead, 0, &smdh, sizeof(SMDH))) && bytesRead == sizeof(SMDH)) {
if(smdh.magic[0] == 'S' && smdh.magic[1] == 'M' && smdh.magic[2] == 'D' && smdh.magic[3] == 'H') {
titleInfo->hasMeta = true;
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
utf16_to_utf8((uint8_t*) item->name, smdh->titles[systemLanguage].shortDescription, NAME_MAX - 1);
utf16_to_utf8((uint8_t*) item->name, smdh.titles[systemLanguage].shortDescription, NAME_MAX - 1);
utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh.titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh.titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh.titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1);
titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh.largeIcon, sizeof(smdh.largeIcon), 48, 48, GPU_RGB565, false);
utf16_to_utf8((uint8_t*) titleInfo->meta.shortDescription, smdh->titles[systemLanguage].shortDescription, sizeof(titleInfo->meta.shortDescription) - 1);
utf16_to_utf8((uint8_t*) titleInfo->meta.longDescription, smdh->titles[systemLanguage].longDescription, sizeof(titleInfo->meta.longDescription) - 1);
utf16_to_utf8((uint8_t*) titleInfo->meta.publisher, smdh->titles[systemLanguage].publisher, sizeof(titleInfo->meta.publisher) - 1);
titleInfo->meta.texture = screen_load_texture_tiled_auto(smdh->largeIcon, sizeof(smdh->largeIcon), 48, 48, GPU_RGB565, false);
}
}
free(smdh);
}
FSFILE_Close(fileHandle);
@ -153,59 +148,63 @@ static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaT
titleInfo->twl = true;
titleInfo->hasMeta = false;
BNR bnr;
if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) &bnr))) {
titleInfo->hasMeta = true;
BNR* bnr = (BNR*) calloc(1, sizeof(BNR));
if(bnr != NULL) {
if(R_SUCCEEDED(FSUSER_GetLegacyBannerData(mediaType, titleId, (u8*) bnr))) {
titleInfo->hasMeta = true;
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
u8 systemLanguage = CFG_LANGUAGE_EN;
CFGU_GetSystemLanguage(&systemLanguage);
char title[0x100] = {'\0'};
utf16_to_utf8((uint8_t*) title, bnr.titles[systemLanguage], sizeof(title) - 1);
char title[0x100] = {'\0'};
utf16_to_utf8((uint8_t*) title, bnr->titles[systemLanguage], sizeof(title) - 1);
if(strchr(title, '\n') == NULL) {
size_t len = strlen(title);
strncpy(item->name, title, len);
strncpy(titleInfo->meta.shortDescription, title, len);
} else {
char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher};
int currDest = 0;
if(strchr(title, '\n') == NULL) {
size_t len = strlen(title);
strncpy(item->name, title, len);
strncpy(titleInfo->meta.shortDescription, title, len);
} else {
char* destinations[] = {titleInfo->meta.shortDescription, titleInfo->meta.longDescription, titleInfo->meta.publisher};
int currDest = 0;
char* last = title;
char* curr = NULL;
char* last = title;
char* curr = NULL;
while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
strncpy(destinations[currDest++], last, curr - last);
last = curr + 1;
*curr = ' ';
while(currDest < 3 && (curr = strchr(last, '\n')) != NULL) {
strncpy(destinations[currDest++], last, curr - last);
last = curr + 1;
*curr = ' ';
}
strncpy(item->name, title, last - title);
if(currDest < 3) {
strncpy(destinations[currDest], last, strlen(title) - (last - title));
}
}
strncpy(item->name, title, last - title);
if(currDest < 3) {
strncpy(destinations[currDest], last, strlen(title) - (last - title));
u8 icon[32 * 32 * 2];
for(u32 x = 0; x < 32; x++) {
for(u32 y = 0; y < 32; y++) {
u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1);
u32 srcShift = (x & 1) * 4;
u16 srcPx = bnr->mainIconPalette[(bnr->mainIconBitmap[srcPos] >> srcShift) & 0xF];
u8 r = (u8) (srcPx & 0x1F);
u8 g = (u8) ((srcPx >> 5) & 0x1F);
u8 b = (u8) ((srcPx >> 10) & 0x1F);
u16 reversedPx = (u16) ((r << 11) | (g << 6) | (b << 1) | 1);
u32 dstPos = (y * 32 + x) * 2;
icon[dstPos + 0] = (u8) (reversedPx & 0xFF);
icon[dstPos + 1] = (u8) ((reversedPx >> 8) & 0xFF);
}
}
titleInfo->meta.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false);
}
u8 icon[32 * 32 * 2];
for(u32 x = 0; x < 32; x++) {
for(u32 y = 0; y < 32; y++) {
u32 srcPos = (((y >> 3) * 4 + (x >> 3)) * 8 + (y & 7)) * 4 + ((x & 7) >> 1);
u32 srcShift = (x & 1) * 4;
u16 srcPx = bnr.mainIconPalette[(bnr.mainIconBitmap[srcPos] >> srcShift) & 0xF];
u8 r = (u8) (srcPx & 0x1F);
u8 g = (u8) ((srcPx >> 5) & 0x1F);
u8 b = (u8) ((srcPx >> 10) & 0x1F);
u16 reversedPx = (u16) ((r << 11) | (g << 6) | (b << 1) | 1);
u32 dstPos = (y * 32 + x) * 2;
icon[dstPos + 0] = (u8) (reversedPx & 0xFF);
icon[dstPos + 1] = (u8) ((reversedPx >> 8) & 0xFF);
}
}
titleInfo->meta.texture = screen_load_texture_auto(icon, sizeof(icon), 32, 32, GPU_RGBA5551, false);
free(bnr);
}
bool empty = strlen(item->name) == 0;
@ -244,6 +243,13 @@ static Result task_populate_titles_add_twl(populate_titles_data* data, FS_MediaT
return res;
}
static int task_populate_titles_compare_ids(const void* e1, const void* e2) {
u64 id1 = *(u64*) e1;
u64 id2 = *(u64*) e2;
return id1 > id2 ? 1 : id1 < id2 ? -1 : 0;
}
static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType mediaType, bool useDSiWare) {
bool inserted;
FS_CardType type;
@ -259,7 +265,7 @@ static Result task_populate_titles_from(populate_titles_data* data, FS_MediaType
u64* titleIds = (u64*) calloc(titleCount, sizeof(u64));
if(titleIds != NULL) {
if(R_SUCCEEDED(res = AM_GetTitleList(&titleCount, mediaType, titleCount, titleIds))) {
qsort(titleIds, titleCount, sizeof(u64), util_compare_u64);
qsort(titleIds, titleCount, sizeof(u64), task_populate_titles_compare_ids);
for(u32 i = 0; i < titleCount && R_SUCCEEDED(res); i++) {
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
@ -292,12 +298,19 @@ static void task_populate_titles_thread(void* arg) {
populate_titles_data* data = (populate_titles_data*) arg;
Result res = 0;
if(R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_GAME_CARD, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_SD, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_NAND, false)) || R_FAILED(res = task_populate_titles_from(data, MEDIATYPE_NAND, true))) {
error_display_res(NULL, NULL, NULL, res, "Failed to load title listing.");
if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_GAME_CARD, false))) {
if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_SD, false))) {
if(R_SUCCEEDED(res = task_populate_titles_from(data, MEDIATYPE_NAND, false))) {
res = task_populate_titles_from(data, MEDIATYPE_NAND, true);
}
}
}
svcCloseHandle(data->cancelEvent);
free(data);
data->result = res;
data->finished = true;
}
void task_free_title(list_item* item) {
@ -332,37 +345,30 @@ void task_clear_titles(linked_list* items) {
}
}
Handle task_populate_titles(linked_list* items) {
if(items == NULL) {
return 0;
Result task_populate_titles(populate_titles_data* data) {
if(data == NULL || data->items == NULL) {
return R_FBI_INVALID_ARGUMENT;
}
task_clear_titles(items);
task_clear_titles(data->items);
populate_titles_data* data = (populate_titles_data*) calloc(1, sizeof(populate_titles_data));
if(data == NULL) {
error_display(NULL, NULL, NULL, "Failed to allocate title list data.");
data->finished = false;
data->result = 0;
data->cancelEvent = 0;
return 0;
Result res = 0;
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, 1))) {
if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) {
res = R_FBI_THREAD_CREATE_FAILED;
}
}
data->items = items;
Result eventRes = svcCreateEvent(&data->cancelEvent, 1);
if(R_FAILED(eventRes)) {
error_display_res(NULL, NULL, NULL, eventRes, "Failed to create title list cancel event.");
free(data);
return 0;
if(R_FAILED(res)) {
if(data->cancelEvent != 0) {
svcCloseHandle(data->cancelEvent);
data->cancelEvent = 0;
}
}
if(threadCreate(task_populate_titles_thread, data, 0x10000, 0x18, 1, true) == NULL) {
error_display(NULL, NULL, NULL, "Failed to create title list thread.");
svcCloseHandle(data->cancelEvent);
free(data);
return 0;
}
return data->cancelEvent;
return res;
}

View File

@ -3,13 +3,6 @@
#define FILE_NAME_MAX 512
#define FILE_PATH_MAX 512
#define R_FBI_CANCELLED MAKERESULT(RL_PERMANENT, RS_CANCELED, RM_APPLICATION, 1)
#define R_FBI_ERRNO MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 2)
#define R_FBI_HTTP_RESPONSE_CODE MAKERESULT(RL_PERMANENT, RS_INTERNAL, RM_APPLICATION, 3)
#define R_FBI_WRONG_SYSTEM MAKERESULT(RL_PERMANENT, RS_NOTSUPPORTED, RM_APPLICATION, 4)
#define R_FBI_OUT_OF_MEMORY MAKERESULT(RL_FATAL, RS_OUTOFRESOURCE, RM_APPLICATION, RD_OUT_OF_MEMORY)
typedef struct linked_list_s linked_list;
typedef struct list_item_s list_item;
@ -62,21 +55,33 @@ typedef struct cia_info_s {
} cia_info;
typedef struct file_info_s {
FS_Archive* archive;
FS_Archive archive;
char name[FILE_NAME_MAX];
char path[FILE_PATH_MAX];
bool isDirectory;
u64 size;
bool containsCias;
u64 size;
bool isCia;
cia_info ciaInfo;
bool containsTickets;
bool isTicket;
ticket_info ticketInfo;
bool containsCias;
bool containsTickets;
} file_info;
typedef struct {
u16* buffer;
s16 width;
s16 height;
Handle mutex;
volatile bool finished;
Result result;
Handle cancelEvent;
} capture_cam_data;
typedef enum data_op_e {
DATAOP_COPY,
DATAOP_DELETE
@ -90,9 +95,6 @@ typedef struct data_op_info_s {
// Copy
bool copyEmpty;
bool finished;
bool premature;
u32 processed;
u32 total;
@ -118,38 +120,97 @@ typedef struct data_op_info_s {
// Errors
bool (*error)(void* data, u32 index, Result res);
} data_op_info;
// General
volatile bool finished;
Result result;
Handle cancelEvent;
} data_op_data;
typedef struct {
linked_list* items;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_ext_save_data_data;
typedef struct {
linked_list* items;
file_info* base;
bool recursive;
bool includeBase;
bool dirsFirst;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_files_data;
typedef struct {
linked_list* items;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_pending_titles_data;
typedef struct {
linked_list* items;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_system_save_data_data;
typedef struct {
linked_list* items;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_tickets_data;
typedef struct {
linked_list* items;
volatile bool finished;
Result result;
Handle cancelEvent;
} populate_titles_data;
void task_init();
void task_exit();
bool task_is_quit_all();
Handle task_get_pause_event();
Handle task_capture_cam(Handle* mutex, u16* buffer, s16 width, s16 height);
Result task_capture_cam(capture_cam_data* data);
Handle task_data_op(data_op_info* info);
Result task_data_op(data_op_data* data);
void task_free_ext_save_data(list_item* item);
void task_clear_ext_save_data(linked_list* items);
Handle task_populate_ext_save_data(linked_list* items);
Result task_populate_ext_save_data(populate_ext_save_data_data* data);
void task_free_file(list_item* item);
void task_clear_files(linked_list* items);
Result task_create_file_item(list_item** out, FS_Archive* archive, const char* path);
Handle task_populate_files(linked_list* items, file_info* dir);
Result task_create_file_item(list_item** out, FS_Archive archive, const char* path);
Result task_populate_files(populate_files_data* data);
void task_free_pending_title(list_item* item);
void task_clear_pending_titles(linked_list* items);
Handle task_populate_pending_titles(linked_list* items);
Result task_populate_pending_titles(populate_pending_titles_data* data);
void task_free_system_save_data(list_item* item);
void task_clear_system_save_data(linked_list* items);
Handle task_populate_system_save_data(linked_list* items);
Result task_populate_system_save_data(populate_system_save_data_data* data);
void task_free_ticket(list_item* item);
void task_clear_tickets(linked_list* items);
Handle task_populate_tickets(linked_list* items);
Result task_populate_tickets(populate_tickets_data* data);
void task_free_title(list_item* item);
void task_clear_titles(linked_list* items);
Handle task_populate_titles(linked_list* items);
Result task_populate_titles(populate_titles_data* data);

View File

@ -16,7 +16,8 @@ static list_item install_from_cdn = {"Install from CDN", COLOR_TEXT, action_inst
static list_item delete_ticket = {"Delete Ticket", COLOR_TEXT, action_delete_ticket};
typedef struct {
Handle cancelEvent;
populate_tickets_data populateData;
bool populated;
} tickets_data;
@ -84,13 +85,11 @@ static void tickets_update(ui_view* view, void* data, linked_list* items, list_i
tickets_data* listData = (tickets_data*) data;
if(hidKeysDown() & KEY_B) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
ui_pop();
@ -103,19 +102,28 @@ static void tickets_update(ui_view* view, void* data, linked_list* items, list_i
}
if(!listData->populated || (hidKeysDown() & KEY_X)) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
listData->cancelEvent = task_populate_tickets(items);
listData->populateData.items = items;
Result res = task_populate_tickets(&listData->populateData);
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate ticket list population.");
}
listData->populated = true;
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate ticket list.");
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
tickets_action_open(items, selected);
return;
@ -130,5 +138,7 @@ void tickets_open() {
return;
}
data->populateData.finished = true;
list_display("Tickets", "A: Select, B: Return, X: Refresh", data, tickets_update, tickets_draw_top);
}

View File

@ -21,7 +21,8 @@ static list_item export_secure_value = {"Export Secure Value", COLOR_TEXT, actio
static list_item delete_secure_value = {"Delete Secure Value", COLOR_TEXT, action_delete_secure_value};
typedef struct {
Handle cancelEvent;
populate_titles_data populateData;
bool populated;
} titles_data;
@ -105,13 +106,11 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it
titles_data* listData = (titles_data*) data;
if(hidKeysDown() & KEY_B) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
ui_pop();
@ -124,19 +123,28 @@ static void titles_update(ui_view* view, void* data, linked_list* items, list_it
}
if(!listData->populated || (hidKeysDown() & KEY_X)) {
if(listData->cancelEvent != 0) {
svcSignalEvent(listData->cancelEvent);
while(svcWaitSynchronization(listData->cancelEvent, 0) == 0) {
if(!listData->populateData.finished) {
svcSignalEvent(listData->populateData.cancelEvent);
while(!listData->populateData.finished) {
svcSleepThread(1000000);
}
listData->cancelEvent = 0;
}
listData->cancelEvent = task_populate_titles(items);
listData->populateData.items = items;
Result res = task_populate_titles(&listData->populateData);
if(R_FAILED(res)) {
error_display_res(NULL, NULL, NULL, res, "Failed to initiate title list population.");
}
listData->populated = true;
}
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
listData->populateData.result = 0;
error_display_res(NULL, NULL, NULL, listData->populateData.result, "Failed to populate title list.");
}
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
titles_action_open(items, selected);
return;
@ -151,5 +159,7 @@ void titles_open() {
return;
}
data->populateData.finished = true;
list_display("Titles", "A: Select, B: Return, X: Refresh", data, titles_update, titles_draw_top);
}