mirror of
https://gitlab.com/Theopse/fbi-i18n-zh.git
synced 2025-04-05 19:41:43 +08:00
Remove TitleDB feature, fix update query filter.
This commit is contained in:
parent
58c7dd0d17
commit
aeb7b9056f
@ -7,8 +7,5 @@ file=FF000000
|
||||
directory=FF0000FF
|
||||
enabled=FF00FF00
|
||||
disabled=FF0000FF
|
||||
titledbinstalled=FF00FF00
|
||||
titledboutdated=FFFF0000
|
||||
titledbnotinstalled=FF0000FF
|
||||
ticketinuse=FF00FF00
|
||||
ticketnotinuse=FF0000FF
|
||||
|
@ -53,8 +53,4 @@ void action_delete_secure_value(linked_list* items, list_item* selected);
|
||||
void action_install_url(const char* confirmMessage, const char* urls, const char* paths, void* userData,
|
||||
void (*finishedURL)(void* data, u32 index),
|
||||
void (*finishedAll)(void* data),
|
||||
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, u32 index));
|
||||
|
||||
void action_install_titledb(linked_list* items, list_item* selected, bool cia);
|
||||
void action_mark_titledb_updated(linked_list* items, list_item* selected, bool cia);
|
||||
void action_update_titledb(linked_list* items, list_item* selected);
|
||||
void (*drawTop)(ui_view* view, void* data, float x1, float y1, float x2, float y2, u32 index));
|
@ -1,86 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "../task/uitask.h"
|
||||
#include "../../core/core.h"
|
||||
|
||||
typedef struct {
|
||||
list_item* selected;
|
||||
bool cia;
|
||||
} install_titledb_data;
|
||||
|
||||
static void action_install_titledb_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, u32 index) {
|
||||
install_titledb_data* installData = (install_titledb_data*) data;
|
||||
|
||||
if(installData->cia) {
|
||||
task_draw_titledb_info_cia(view, installData->selected->data, x1, y1, x2, y2);
|
||||
} else {
|
||||
task_draw_titledb_info_tdsx(view, installData->selected->data, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
static void action_install_titledb_finished_url(void* data, u32 index) {
|
||||
install_titledb_data* installData = (install_titledb_data*) data;
|
||||
list_item* item = installData->selected;
|
||||
titledb_info* info = (titledb_info*) item->data;
|
||||
|
||||
titledb_cache_entry entry;
|
||||
if(installData->cia) {
|
||||
entry.id = info->cia.id;
|
||||
string_copy(entry.mtime, info->cia.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->cia.version, sizeof(entry.version));
|
||||
} else {
|
||||
entry.id = info->tdsx.id;
|
||||
string_copy(entry.mtime, info->tdsx.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->tdsx.version, sizeof(entry.version));
|
||||
}
|
||||
|
||||
task_populate_titledb_cache_set(info->id, installData->cia, &entry);
|
||||
task_populate_titledb_update_status(item);
|
||||
}
|
||||
|
||||
static void action_install_titledb_finished_all(void* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void action_install_titledb(linked_list* items, list_item* selected, bool cia) {
|
||||
install_titledb_data* data = (install_titledb_data*) calloc(1, sizeof(install_titledb_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate install TitleDB data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->selected = selected;
|
||||
data->cia = cia;
|
||||
|
||||
titledb_info* info = (titledb_info*) selected->data;
|
||||
|
||||
char urls[2 * DOWNLOAD_URL_MAX];
|
||||
char paths[2 * FILE_PATH_MAX];
|
||||
if(data->cia) {
|
||||
snprintf(urls, sizeof(urls), "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id);
|
||||
} else {
|
||||
char filePath[FILE_PATH_MAX];
|
||||
fs_make_3dsx_path(filePath, info->meta.shortDescription, sizeof(filePath));
|
||||
|
||||
u32 urlsPos = 0;
|
||||
u32 pathsPos = 0;
|
||||
|
||||
urlsPos += snprintf(urls + urlsPos, sizeof(urls) - urlsPos, "https://3ds.titledb.com/v1/tdsx/%lu/download\n", info->tdsx.id);
|
||||
pathsPos += snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "%s\n", filePath);
|
||||
|
||||
if(info->tdsx.smdh.exists) {
|
||||
fs_make_smdh_path(filePath, info->meta.shortDescription, sizeof(filePath));
|
||||
|
||||
snprintf(urls + urlsPos, sizeof(urls) - urlsPos, "https://3ds.titledb.com/v1/smdh/%lu/download\n", info->tdsx.smdh.id);
|
||||
snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "%s\n", filePath);
|
||||
}
|
||||
}
|
||||
|
||||
action_install_url("Install the selected title from TitleDB?", urls, paths, data, action_install_titledb_finished_url, action_install_titledb_finished_all, action_install_titledb_draw_top);
|
||||
}
|
@ -1,27 +0,0 @@
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "../task/uitask.h"
|
||||
#include "../../core/core.h"
|
||||
|
||||
void action_mark_titledb_updated(linked_list* items, list_item* selected, bool cia) {
|
||||
titledb_info* info = (titledb_info*) selected->data;
|
||||
|
||||
if((cia && info->cia.installed) || (!cia && info->tdsx.installed)) {
|
||||
titledb_cache_entry entry;
|
||||
if(cia) {
|
||||
entry.id = info->cia.id;
|
||||
string_copy(entry.mtime, info->cia.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->cia.version, sizeof(entry.version));
|
||||
} else {
|
||||
entry.id = info->tdsx.id;
|
||||
string_copy(entry.mtime, info->tdsx.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->tdsx.version, sizeof(entry.version));
|
||||
}
|
||||
|
||||
task_populate_titledb_cache_set(info->id, cia, &entry);
|
||||
task_populate_titledb_update_status(selected);
|
||||
}
|
||||
}
|
@ -1,115 +0,0 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "action.h"
|
||||
#include "../task/uitask.h"
|
||||
#include "../../core/core.h"
|
||||
|
||||
typedef struct {
|
||||
char urls[INSTALL_URLS_MAX * DOWNLOAD_URL_MAX];
|
||||
char paths[INSTALL_URLS_MAX * FILE_PATH_MAX];
|
||||
|
||||
bool cia[INSTALL_URLS_MAX];
|
||||
list_item* items[INSTALL_URLS_MAX];
|
||||
} update_titledb_data;
|
||||
|
||||
static void action_update_titledb_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, u32 index) {
|
||||
update_titledb_data* updateData = (update_titledb_data*) data;
|
||||
|
||||
if(updateData->items[index] != NULL) {
|
||||
if(updateData->cia[index]) {
|
||||
task_draw_titledb_info_cia(view, updateData->items[index]->data, x1, y1, x2, y2);
|
||||
} else {
|
||||
task_draw_titledb_info_tdsx(view, updateData->items[index]->data, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void action_update_titledb_finished_url(void* data, u32 index) {
|
||||
update_titledb_data* updateData = (update_titledb_data*) data;
|
||||
list_item* item = updateData->items[index];
|
||||
titledb_info* info = (titledb_info*) item->data;
|
||||
|
||||
titledb_cache_entry entry;
|
||||
if(updateData->cia[index]) {
|
||||
entry.id = info->cia.id;
|
||||
string_copy(entry.mtime, info->cia.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->cia.version, sizeof(entry.version));
|
||||
} else {
|
||||
entry.id = info->tdsx.id;
|
||||
string_copy(entry.mtime, info->tdsx.mtime, sizeof(entry.mtime));
|
||||
string_copy(entry.version, info->tdsx.version, sizeof(entry.version));
|
||||
}
|
||||
|
||||
task_populate_titledb_cache_set(info->id, updateData->cia[index], &entry);
|
||||
task_populate_titledb_update_status(item);
|
||||
}
|
||||
|
||||
static void action_update_titledb_finished_all(void* data) {
|
||||
free(data);
|
||||
}
|
||||
|
||||
void action_update_titledb(linked_list* items, list_item* selected) {
|
||||
update_titledb_data* data = (update_titledb_data*) calloc(1, sizeof(update_titledb_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate install TitleDB data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
|
||||
u32 index = 0;
|
||||
u32 urlsPos = 0;
|
||||
u32 pathsPos = 0;
|
||||
while(linked_list_iter_has_next(&iter) && index < INSTALL_URLS_MAX && urlsPos < INSTALL_URLS_MAX * DOWNLOAD_URL_MAX && pathsPos < INSTALL_URLS_MAX * FILE_PATH_MAX) {
|
||||
list_item* item = linked_list_iter_next(&iter);
|
||||
titledb_info* info = (titledb_info*) item->data;
|
||||
|
||||
if(info->cia.installed && info->cia.installedInfo.id != info->cia.id) {
|
||||
urlsPos += snprintf(data->urls + urlsPos, INSTALL_URLS_MAX * DOWNLOAD_URL_MAX - urlsPos,
|
||||
"https://3ds.titledb.com/v1/cia/%lu/download\n",
|
||||
info->cia.id);
|
||||
pathsPos += snprintf(data->paths + pathsPos, INSTALL_URLS_MAX * FILE_PATH_MAX - pathsPos,
|
||||
"\n");
|
||||
|
||||
data->cia[index] = true;
|
||||
data->items[index] = item;
|
||||
|
||||
index++;
|
||||
}
|
||||
|
||||
if(info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id && (!info->tdsx.smdh.exists || index < INSTALL_URLS_MAX - 1)) {
|
||||
char filePath[FILE_PATH_MAX];
|
||||
fs_make_3dsx_path(filePath, info->meta.shortDescription, sizeof(filePath));
|
||||
|
||||
urlsPos += snprintf(data->urls + urlsPos, INSTALL_URLS_MAX * DOWNLOAD_URL_MAX - urlsPos, "https://3ds.titledb.com/v1/tdsx/%lu/download\n", info->tdsx.id);
|
||||
pathsPos += snprintf(data->paths + pathsPos, INSTALL_URLS_MAX * FILE_PATH_MAX - pathsPos, "%s\n", filePath);
|
||||
data->cia[index] = false;
|
||||
data->items[index] = item;
|
||||
|
||||
index++;
|
||||
|
||||
if(info->tdsx.smdh.exists) {
|
||||
fs_make_smdh_path(filePath, info->meta.shortDescription, sizeof(filePath));
|
||||
|
||||
urlsPos += snprintf(data->urls + urlsPos, INSTALL_URLS_MAX * DOWNLOAD_URL_MAX - urlsPos, "https://3ds.titledb.com/v1/smdh/%lu/download\n", info->tdsx.smdh.id);
|
||||
pathsPos += snprintf(data->paths + pathsPos, INSTALL_URLS_MAX * FILE_PATH_MAX - pathsPos, "%s\n", filePath);
|
||||
data->cia[index] = false;
|
||||
data->items[index] = item;
|
||||
|
||||
index++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(index > 0) {
|
||||
action_install_url("Install all updates from TitleDB?", data->urls, data->paths, data, action_update_titledb_finished_url, action_update_titledb_finished_all, action_update_titledb_draw_top);
|
||||
} else {
|
||||
prompt_display_notify("Success", "All titles are up to date.", COLOR_TEXT, NULL, NULL, NULL);
|
||||
}
|
||||
}
|
@ -155,7 +155,6 @@ void init() {
|
||||
|
||||
void cleanup() {
|
||||
clipboard_clear();
|
||||
task_populate_titledb_cache_unload();
|
||||
|
||||
task_exit();
|
||||
ui_exit();
|
||||
|
@ -18,7 +18,6 @@ static list_item pending_titles = {"Pending Titles", COLOR_TEXT, pendingtitles_o
|
||||
static list_item tickets = {"Tickets", COLOR_TEXT, tickets_open};
|
||||
static list_item ext_save_data = {"Ext Save Data", COLOR_TEXT, extsavedata_open};
|
||||
static list_item system_save_data = {"System Save Data", COLOR_TEXT, systemsavedata_open};
|
||||
static list_item titledb = {"TitleDB", COLOR_TEXT, titledb_open};
|
||||
static list_item remote_install = {"Remote Install", COLOR_TEXT, remoteinstall_open};
|
||||
static list_item update = {"Update", COLOR_TEXT, update_open};
|
||||
|
||||
@ -57,7 +56,6 @@ static void mainmenu_update(ui_view* view, void* data, linked_list* items, list_
|
||||
linked_list_add(items, &tickets);
|
||||
linked_list_add(items, &ext_save_data);
|
||||
linked_list_add(items, &system_save_data);
|
||||
linked_list_add(items, &titledb);
|
||||
linked_list_add(items, &remote_install);
|
||||
linked_list_add(items, &update);
|
||||
}
|
||||
|
@ -66,12 +66,6 @@ void resources_load() {
|
||||
screen_set_color(COLOR_ENABLED, color);
|
||||
} else if(strcasecmp(key, "disabled") == 0) {
|
||||
screen_set_color(COLOR_DISABLED, color);
|
||||
} else if(strcasecmp(key, "titledbinstalled") == 0) {
|
||||
screen_set_color(COLOR_TITLEDB_INSTALLED, color);
|
||||
} else if(strcasecmp(key, "titledboutdated") == 0) {
|
||||
screen_set_color(COLOR_TITLEDB_OUTDATED, color);
|
||||
} else if(strcasecmp(key, "titledbnotinstalled") == 0) {
|
||||
screen_set_color(COLOR_TITLEDB_NOT_INSTALLED, color);
|
||||
} else if(strcasecmp(key, "ticketinuse") == 0) {
|
||||
screen_set_color(COLOR_TICKET_IN_USE, color);
|
||||
} else if(strcasecmp(key, "ticketnotinuse") == 0) {
|
||||
|
@ -39,10 +39,7 @@
|
||||
#define COLOR_DIRECTORY 6
|
||||
#define COLOR_ENABLED 7
|
||||
#define COLOR_DISABLED 8
|
||||
#define COLOR_TITLEDB_INSTALLED 9
|
||||
#define COLOR_TITLEDB_OUTDATED 10
|
||||
#define COLOR_TITLEDB_NOT_INSTALLED 11
|
||||
#define COLOR_TICKET_IN_USE 12
|
||||
#define COLOR_TICKET_NOT_IN_USE 13
|
||||
#define COLOR_TICKET_IN_USE 9
|
||||
#define COLOR_TICKET_NOT_IN_USE 10
|
||||
|
||||
void resources_load();
|
@ -15,5 +15,4 @@ void remoteinstall_open();
|
||||
void systemsavedata_open();
|
||||
void tickets_open();
|
||||
void titles_open();
|
||||
void titledb_open();
|
||||
void update_open();
|
@ -1,453 +0,0 @@
|
||||
#include <sys/stat.h>
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
#include <jansson.h>
|
||||
|
||||
#include "uitask.h"
|
||||
#include "listtitledb.h"
|
||||
#include "../resources.h"
|
||||
#include "../../core/core.h"
|
||||
#include "../../core/task/dataop.h"
|
||||
|
||||
#define json_object_get_string(obj, name, def) (json_is_string(json_object_get(obj, name)) ? json_string_value(json_object_get(obj, name)) : def)
|
||||
#define json_object_get_integer(obj, name, def) (json_is_integer(json_object_get(obj, name)) ? json_integer_value(json_object_get(obj, name)) : def)
|
||||
|
||||
#define TITLEDB_CACHE_DIR "sdmc:/fbi/"
|
||||
#define TITLEDB_CACHE_FILE TITLEDB_CACHE_DIR "titledb_cache.json"
|
||||
|
||||
static json_t* installedApps = NULL;
|
||||
|
||||
static void task_populate_titledb_cache_load() {
|
||||
if(installedApps != NULL) {
|
||||
json_decref(installedApps);
|
||||
}
|
||||
|
||||
json_error_t error;
|
||||
installedApps = json_load_file(TITLEDB_CACHE_FILE, 0, &error);
|
||||
|
||||
if(!json_is_object(installedApps)) {
|
||||
if(installedApps != NULL) {
|
||||
json_decref(installedApps);
|
||||
}
|
||||
|
||||
installedApps = json_object();
|
||||
}
|
||||
}
|
||||
|
||||
static void task_populate_titledb_cache_save() {
|
||||
if(json_is_object(installedApps)) {
|
||||
mkdir(TITLEDB_CACHE_DIR, 755);
|
||||
|
||||
json_dump_file(installedApps, TITLEDB_CACHE_FILE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
void task_populate_titledb_cache_unload() {
|
||||
if(json_is_object(installedApps)) {
|
||||
task_populate_titledb_cache_save();
|
||||
|
||||
json_decref(installedApps);
|
||||
installedApps = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static json_t* task_populate_titledb_cache_get_base(u32 id, bool cia, bool create) {
|
||||
if(!json_is_object(installedApps)) {
|
||||
task_populate_titledb_cache_load();
|
||||
}
|
||||
|
||||
if(json_is_object(installedApps)) {
|
||||
char idString[16];
|
||||
itoa(id, idString, 10);
|
||||
|
||||
json_t* cache = json_object_get(installedApps, idString);
|
||||
if(!json_is_object(cache)) {
|
||||
if(create) {
|
||||
cache = json_object();
|
||||
json_object_set(installedApps, idString, cache);
|
||||
} else {
|
||||
cache = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
if(json_is_object(cache)) {
|
||||
// Get old cache entry.
|
||||
const char* objIdKey = cia ? "cia_id" : "tdsx_id";
|
||||
json_t* objId = json_object_get(cache, objIdKey);
|
||||
|
||||
const char* objKey = cia ? "cia" : "tdsx";
|
||||
json_t* obj = json_object_get(cache, objKey);
|
||||
if(!json_is_object(obj)) {
|
||||
// Force creation if old value to migrate exists.
|
||||
if(create || json_is_integer(objId)) {
|
||||
obj = json_object();
|
||||
json_object_set(cache, objKey, obj);
|
||||
} else {
|
||||
obj = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
// Migrate old cache entry.
|
||||
if(json_is_integer(objId)) {
|
||||
json_object_set(obj, "id", json_integer(json_integer_value(objId)));
|
||||
json_object_del(cache, objIdKey);
|
||||
}
|
||||
|
||||
return obj;
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static bool task_populate_titledb_cache_get(u32 id, bool cia, titledb_cache_entry* entry) {
|
||||
json_t* obj = task_populate_titledb_cache_get_base(id, cia, false);
|
||||
if(json_is_object(obj)) {
|
||||
json_t* idJson = json_object_get(obj, "id");
|
||||
if(json_is_integer(idJson)) {
|
||||
entry->id = (u32) json_integer_value(idJson);
|
||||
string_copy(entry->mtime, json_object_get_string(obj, "mtime", "Unknown"), sizeof(entry->mtime));
|
||||
string_copy(entry->version, json_object_get_string(obj, "version", "Unknown"), sizeof(entry->version));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void task_populate_titledb_cache_set(u32 id, bool cia, titledb_cache_entry* entry) {
|
||||
json_t* obj = task_populate_titledb_cache_get_base(id, cia, true);
|
||||
if(json_is_object(obj)) {
|
||||
json_object_set(obj, "id", json_integer(entry->id));
|
||||
json_object_set(obj, "mtime", json_string(entry->mtime));
|
||||
json_object_set(obj, "version", json_string(entry->version));
|
||||
|
||||
task_populate_titledb_cache_save();
|
||||
}
|
||||
}
|
||||
|
||||
void task_populate_titledb_update_status(list_item* item) {
|
||||
titledb_info* info = (titledb_info*) item->data;
|
||||
|
||||
info->cia.installed = false;
|
||||
info->tdsx.installed = false;
|
||||
|
||||
if(info->cia.exists) {
|
||||
AM_TitleEntry entry;
|
||||
info->cia.installed = R_SUCCEEDED(AM_GetTitleInfo(fs_get_title_destination(info->cia.titleId), 1, &info->cia.titleId, &entry))
|
||||
&& task_populate_titledb_cache_get(info->id, true, &info->cia.installedInfo);
|
||||
}
|
||||
|
||||
if(info->tdsx.exists) {
|
||||
char path3dsx[FILE_PATH_MAX];
|
||||
fs_make_3dsx_path(path3dsx, info->meta.shortDescription, sizeof(path3dsx));
|
||||
|
||||
FS_Path* fsPath = fs_make_path_utf8(path3dsx);
|
||||
if(fsPath != NULL) {
|
||||
Handle handle = 0;
|
||||
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) {
|
||||
FSFILE_Close(handle);
|
||||
|
||||
info->tdsx.installed = task_populate_titledb_cache_get(info->id, false, &info->tdsx.installedInfo);
|
||||
}
|
||||
|
||||
fs_free_path_utf8(fsPath);
|
||||
}
|
||||
}
|
||||
|
||||
if((info->cia.installed && info->cia.installedInfo.id != info->cia.id) || (info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id)) {
|
||||
item->color = COLOR_TITLEDB_OUTDATED;
|
||||
} else if(info->cia.installed || info->tdsx.installed) {
|
||||
item->color = COLOR_TITLEDB_INSTALLED;
|
||||
} else {
|
||||
item->color = COLOR_TITLEDB_NOT_INSTALLED;
|
||||
}
|
||||
}
|
||||
|
||||
static int task_populate_titledb_compare_dates(const char* date1, const char* date2, size_t size) {
|
||||
bool unk1 = strncmp(date1, "Unknown", size) == 0;
|
||||
bool unk2 = strncmp(date2, "Unknown", size) == 0;
|
||||
|
||||
if(unk1 && !unk2) {
|
||||
return -1;
|
||||
} else if(!unk1 && unk2) {
|
||||
return 1;
|
||||
} else if(unk1 && unk2) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return strncmp(date1, date2, size);
|
||||
}
|
||||
|
||||
static void task_populate_titledb_thread(void* arg) {
|
||||
populate_titledb_data* data = (populate_titledb_data*) arg;
|
||||
|
||||
Result res = 0;
|
||||
|
||||
linked_list titles;
|
||||
linked_list_init(&titles);
|
||||
|
||||
json_t* root = NULL;
|
||||
if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true"
|
||||
"&only=id&only=name&only=author&only=headline&only=category"
|
||||
"&only=cia.id&only=cia.mtime&only=cia.version&only=cia.size&only=cia.titleid"
|
||||
"&only=tdsx.id&only=tdsx.mtime&only=tdsx.version&only=tdsx.size&only=tdsx.smdh.id",
|
||||
&root, 1024 * 1024))) {
|
||||
if(json_is_array(root)) {
|
||||
for(u32 i = 0; i < json_array_size(root) && R_SUCCEEDED(res); i++) {
|
||||
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
|
||||
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
json_t* entry = json_array_get(root, i);
|
||||
if(json_is_object(entry)) {
|
||||
list_item* item = (list_item*) calloc(1, sizeof(list_item));
|
||||
if(item != NULL) {
|
||||
titledb_info* titledbInfo = (titledb_info*) calloc(1, sizeof(titledb_info));
|
||||
if(titledbInfo != NULL) {
|
||||
titledbInfo->id = (u32) json_object_get_integer(entry, "id", 0);
|
||||
string_copy(titledbInfo->category, json_object_get_string(entry, "category", "Unknown"), sizeof(titledbInfo->category));
|
||||
string_copy(titledbInfo->meta.shortDescription, json_object_get_string(entry, "name", ""), sizeof(titledbInfo->meta.shortDescription));
|
||||
string_copy(titledbInfo->meta.publisher, json_object_get_string(entry, "author", ""), sizeof(titledbInfo->meta.publisher));
|
||||
|
||||
json_t* headline = json_object_get(entry, "headline");
|
||||
if(json_is_string(headline)) {
|
||||
const char* val = json_string_value(headline);
|
||||
|
||||
if(json_string_length(headline) > sizeof(titledbInfo->headline) - 1) {
|
||||
snprintf(titledbInfo->headline, sizeof(titledbInfo->headline), "%.508s...", val);
|
||||
} else {
|
||||
string_copy(titledbInfo->headline, val, sizeof(titledbInfo->headline));
|
||||
}
|
||||
} else {
|
||||
titledbInfo->headline[0] = '\0';
|
||||
}
|
||||
|
||||
json_t* cias = json_object_get(entry, "cia");
|
||||
if(json_is_array(cias)) {
|
||||
for(u32 j = 0; j < json_array_size(cias); j++) {
|
||||
json_t* cia = json_array_get(cias, j);
|
||||
if(json_is_object(cia)) {
|
||||
const char* mtime = json_object_get_string(cia, "mtime", "Unknown");
|
||||
if(!titledbInfo->cia.exists || task_populate_titledb_compare_dates(mtime, titledbInfo->cia.mtime, sizeof(titledbInfo->cia.mtime)) >= 0) {
|
||||
titledbInfo->cia.exists = true;
|
||||
|
||||
titledbInfo->cia.id = (u32) json_object_get_integer(cia, "id", 0);
|
||||
string_copy(titledbInfo->cia.mtime, mtime, sizeof(titledbInfo->cia.mtime));
|
||||
string_copy(titledbInfo->cia.version, json_object_get_string(cia, "version", "Unknown"), sizeof(titledbInfo->cia.version));
|
||||
titledbInfo->cia.size = (u32) json_object_get_integer(cia, "size", 0);
|
||||
titledbInfo->cia.titleId = strtoull(json_object_get_string(cia, "titleid", "0"), NULL, 16);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
json_t* tdsxs = json_object_get(entry, "tdsx");
|
||||
if(json_is_array(tdsxs)) {
|
||||
for(u32 j = 0; j < json_array_size(tdsxs); j++) {
|
||||
json_t* tdsx = json_array_get(tdsxs, j);
|
||||
if(json_is_object(tdsx)) {
|
||||
const char* mtime = json_object_get_string(tdsx, "mtime", "Unknown");
|
||||
if(!titledbInfo->tdsx.exists || task_populate_titledb_compare_dates(mtime, titledbInfo->tdsx.mtime, sizeof(titledbInfo->tdsx.mtime)) >= 0) {
|
||||
titledbInfo->tdsx.exists = true;
|
||||
|
||||
titledbInfo->tdsx.id = (u32) json_object_get_integer(tdsx, "id", 0);
|
||||
string_copy(titledbInfo->tdsx.mtime, mtime, sizeof(titledbInfo->tdsx.mtime));
|
||||
string_copy(titledbInfo->tdsx.version, json_object_get_string(tdsx, "version", "Unknown"), sizeof(titledbInfo->tdsx.version));
|
||||
titledbInfo->tdsx.size = (u32) json_object_get_integer(tdsx, "size", 0);
|
||||
|
||||
json_t* smdh = json_object_get(tdsx, "smdh");
|
||||
if(json_is_object(smdh)) {
|
||||
titledbInfo->tdsx.smdh.exists = true;
|
||||
|
||||
titledbInfo->tdsx.smdh.id = (u32) json_object_get_integer(smdh, "id", 0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char* latestTime = "Unknown";
|
||||
if(titledbInfo->cia.exists && titledbInfo->tdsx.exists) {
|
||||
if(task_populate_titledb_compare_dates(titledbInfo->cia.mtime, titledbInfo->tdsx.mtime, sizeof(titledbInfo->cia.mtime)) >= 0) {
|
||||
latestTime = titledbInfo->cia.mtime;
|
||||
} else {
|
||||
latestTime = titledbInfo->tdsx.mtime;
|
||||
}
|
||||
} else if(titledbInfo->cia.exists) {
|
||||
latestTime = titledbInfo->cia.mtime;
|
||||
} else if(titledbInfo->tdsx.exists) {
|
||||
latestTime = titledbInfo->tdsx.mtime;
|
||||
}
|
||||
|
||||
string_copy(titledbInfo->mtime, latestTime, sizeof(titledbInfo->mtime));
|
||||
|
||||
if((titledbInfo->cia.exists || titledbInfo->tdsx.exists) && (data->filter == NULL || data->filter(data->userData, titledbInfo))) {
|
||||
string_copy(item->name, titledbInfo->meta.shortDescription, LIST_ITEM_NAME_MAX);
|
||||
item->data = titledbInfo;
|
||||
|
||||
task_populate_titledb_update_status(item);
|
||||
|
||||
linked_list_add_sorted(&titles, item, data->userData, data->compare);
|
||||
} else {
|
||||
free(titledbInfo);
|
||||
free(item);
|
||||
}
|
||||
} else {
|
||||
free(item);
|
||||
|
||||
res = R_APP_OUT_OF_MEMORY;
|
||||
}
|
||||
} else {
|
||||
res = R_APP_OUT_OF_MEMORY;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(&titles, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
list_item* item = linked_list_iter_next(&iter);
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
linked_list_add(data->items, item);
|
||||
} else {
|
||||
task_free_titledb(item);
|
||||
linked_list_iter_remove(&iter);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
res = R_APP_BAD_DATA;
|
||||
}
|
||||
|
||||
json_decref(root);
|
||||
}
|
||||
|
||||
data->itemsListed = true;
|
||||
|
||||
if(R_SUCCEEDED(res)) {
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(&titles, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
svcWaitSynchronization(task_get_pause_event(), U64_MAX);
|
||||
|
||||
Handle events[2] = {data->resumeEvent, data->cancelEvent};
|
||||
s32 index = 0;
|
||||
svcWaitSynchronizationN(&index, events, 2, false, U64_MAX);
|
||||
|
||||
if(task_is_quit_all() || svcWaitSynchronization(data->cancelEvent, 0) == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
list_item* item = (list_item*) linked_list_iter_next(&iter);
|
||||
titledb_info* titledbInfo = (titledb_info*) item->data;
|
||||
|
||||
char url[128];
|
||||
if(titledbInfo->cia.exists) {
|
||||
snprintf(url, sizeof(url), "https://3ds.titledb.com/v1/cia/%lu/icon_l.bin", titledbInfo->cia.id);
|
||||
} else if(titledbInfo->tdsx.exists && titledbInfo->tdsx.smdh.exists) {
|
||||
snprintf(url, sizeof(url), "https://3ds.titledb.com/v1/smdh/%lu/icon_l.bin", titledbInfo->tdsx.smdh.id);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
u8 icon[0x1200];
|
||||
u32 iconSize = 0;
|
||||
if(R_SUCCEEDED(http_download(url, &iconSize, &icon, sizeof(icon))) && iconSize == sizeof(icon)) {
|
||||
titledbInfo->meta.texture = screen_allocate_free_texture();
|
||||
screen_load_texture_tiled(titledbInfo->meta.texture, icon, sizeof(icon), 48, 48, GPU_RGB565, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
linked_list_destroy(&titles);
|
||||
|
||||
svcCloseHandle(data->resumeEvent);
|
||||
svcCloseHandle(data->cancelEvent);
|
||||
|
||||
data->result = res;
|
||||
data->finished = true;
|
||||
}
|
||||
|
||||
void task_free_titledb(list_item* item) {
|
||||
if(item == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(item->data != NULL) {
|
||||
titledb_info* titledbInfo = (titledb_info*) item->data;
|
||||
if(titledbInfo->meta.texture != 0) {
|
||||
screen_unload_texture(titledbInfo->meta.texture);
|
||||
titledbInfo->meta.texture = 0;
|
||||
}
|
||||
|
||||
free(item->data);
|
||||
}
|
||||
|
||||
free(item);
|
||||
}
|
||||
|
||||
void task_clear_titledb(linked_list* items) {
|
||||
if(items == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
list_item* item = (list_item*) linked_list_iter_next(&iter);
|
||||
|
||||
linked_list_iter_remove(&iter);
|
||||
task_free_titledb(item);
|
||||
}
|
||||
}
|
||||
|
||||
Result task_populate_titledb(populate_titledb_data* data) {
|
||||
if(data == NULL || data->items == NULL) {
|
||||
return R_APP_INVALID_ARGUMENT;
|
||||
}
|
||||
|
||||
task_clear_titledb(data->items);
|
||||
|
||||
data->itemsListed = false;
|
||||
data->finished = false;
|
||||
data->result = 0;
|
||||
data->cancelEvent = 0;
|
||||
|
||||
Result res = 0;
|
||||
if(R_SUCCEEDED(res = svcCreateEvent(&data->cancelEvent, RESET_STICKY))) {
|
||||
if(R_SUCCEEDED(res = svcCreateEvent(&data->resumeEvent, RESET_STICKY))) {
|
||||
svcSignalEvent(data->resumeEvent);
|
||||
|
||||
if(threadCreate(task_populate_titledb_thread, data, 0x10000, 0x19, 1, true) == NULL) {
|
||||
res = R_APP_THREAD_CREATE_FAILED;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(R_FAILED(res)) {
|
||||
data->itemsListed = true;
|
||||
data->finished = true;
|
||||
|
||||
if(data->resumeEvent != 0) {
|
||||
svcCloseHandle(data->resumeEvent);
|
||||
data->resumeEvent = 0;
|
||||
}
|
||||
|
||||
if(data->cancelEvent != 0) {
|
||||
svcCloseHandle(data->cancelEvent);
|
||||
data->cancelEvent = 0;
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
@ -1,75 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
typedef struct linked_list_s linked_list;
|
||||
typedef struct list_item_s list_item;
|
||||
|
||||
typedef struct titledb_cache_entry_s {
|
||||
u32 id;
|
||||
char mtime[32];
|
||||
char version[32];
|
||||
} titledb_cache_entry;
|
||||
|
||||
typedef struct titledb_cia_info_s {
|
||||
bool exists;
|
||||
|
||||
u32 id;
|
||||
char mtime[32];
|
||||
char version[32];
|
||||
u64 size;
|
||||
u64 titleId;
|
||||
|
||||
bool installed;
|
||||
titledb_cache_entry installedInfo;
|
||||
} titledb_cia_info;
|
||||
|
||||
typedef struct titledb_smdh_info_s {
|
||||
bool exists;
|
||||
|
||||
u32 id;
|
||||
} titledb_smdh_info;
|
||||
|
||||
typedef struct titledb_tdsx_info_s {
|
||||
bool exists;
|
||||
|
||||
u32 id;
|
||||
char mtime[32];
|
||||
char version[32];
|
||||
u64 size;
|
||||
titledb_smdh_info smdh;
|
||||
|
||||
bool installed;
|
||||
titledb_cache_entry installedInfo;
|
||||
} titledb_tdsx_info;
|
||||
|
||||
typedef struct titledb_info_s {
|
||||
u32 id;
|
||||
char category[64];
|
||||
char headline[512];
|
||||
char mtime[32];
|
||||
titledb_cia_info cia;
|
||||
titledb_tdsx_info tdsx;
|
||||
|
||||
meta_info meta;
|
||||
} titledb_info;
|
||||
|
||||
typedef struct populate_titledb_data_s {
|
||||
linked_list* items;
|
||||
|
||||
void* userData;
|
||||
bool (*filter)(void* data, titledb_info* info);
|
||||
int (*compare)(void* data, const void* p1, const void* p2);
|
||||
|
||||
volatile bool itemsListed;
|
||||
volatile bool finished;
|
||||
Result result;
|
||||
Handle cancelEvent;
|
||||
Handle resumeEvent;
|
||||
} populate_titledb_data;
|
||||
|
||||
void task_populate_titledb_cache_unload();
|
||||
void task_populate_titledb_cache_set(u32 id, bool cia, titledb_cache_entry* entry);
|
||||
|
||||
void task_populate_titledb_update_status(list_item* item);
|
||||
void task_free_titledb(list_item* item);
|
||||
void task_clear_titledb(linked_list* items);
|
||||
Result task_populate_titledb(populate_titledb_data* data);
|
@ -257,119 +257,6 @@ void task_draw_title_info(ui_view* view, void* data, float x1, float y1, float x
|
||||
float infoWidth;
|
||||
screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f);
|
||||
|
||||
float infoX = x1 + (x2 - x1 - infoWidth) / 2;
|
||||
float infoY = y1 + (y2 - y1) / 2 - 8;
|
||||
screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true);
|
||||
}
|
||||
|
||||
static void task_format_date(char* out, const char* date, size_t size) {
|
||||
if(strncmp(date, "Unknown", size) == 0) {
|
||||
string_copy(out, date, size);
|
||||
} else {
|
||||
char updatedDate[16] = "";
|
||||
char updatedTime[16] = "";
|
||||
sscanf(date, "%31[^T]T%31[^Z]Z", updatedDate, updatedTime);
|
||||
|
||||
snprintf(out, size, "%s %s", updatedDate, updatedTime);
|
||||
}
|
||||
}
|
||||
|
||||
void task_draw_titledb_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
|
||||
titledb_info* info = (titledb_info*) data;
|
||||
|
||||
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
|
||||
|
||||
char mtime[32];
|
||||
task_format_date(mtime, info->mtime, sizeof(mtime));
|
||||
|
||||
char infoText[1024];
|
||||
|
||||
snprintf(infoText, sizeof(infoText),
|
||||
"%s\n"
|
||||
"\n"
|
||||
"Category: %s\n"
|
||||
"Updated At: %s\n"
|
||||
"Update Available: %s",
|
||||
info->headline,
|
||||
info->category,
|
||||
mtime,
|
||||
(info->cia.installed && info->cia.installedInfo.id != info->cia.id) || (info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id) ? "Yes" : "No");
|
||||
|
||||
float infoWidth;
|
||||
screen_get_string_size_wrap(&infoWidth, NULL, infoText, 0.5f, 0.5f, x2 - x1 - 10);
|
||||
|
||||
float infoX = x1 + (x2 - x1 - infoWidth) / 2;
|
||||
float infoY = y1 + (y2 - y1) / 2 - 8;
|
||||
screen_draw_string_wrap(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true, infoX + infoWidth + 1);
|
||||
}
|
||||
|
||||
void task_draw_titledb_info_cia(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
|
||||
titledb_info* info = (titledb_info*) data;
|
||||
|
||||
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
|
||||
|
||||
char dbMtime[32];
|
||||
task_format_date(dbMtime, info->cia.mtime, sizeof(dbMtime));
|
||||
|
||||
char localMtime[32];
|
||||
task_format_date(localMtime, info->cia.installedInfo.mtime, sizeof(localMtime));
|
||||
|
||||
char infoText[512];
|
||||
|
||||
snprintf(infoText, sizeof(infoText),
|
||||
"Title ID: %016llX\n"
|
||||
"Size: %.2f %s\n"
|
||||
"TitleDB Updated At: %s\n"
|
||||
"Local Updated At: %s\n"
|
||||
"TitleDB Version: %s\n"
|
||||
"Local Version: %s\n"
|
||||
"Update Available: %s",
|
||||
info->cia.titleId,
|
||||
ui_get_display_size(info->cia.size), ui_get_display_size_units(info->cia.size),
|
||||
dbMtime,
|
||||
info->cia.installed ? localMtime : "Not Installed",
|
||||
info->cia.version,
|
||||
info->cia.installed ? info->cia.installedInfo.version : "Not Installed",
|
||||
info->cia.installed && info->cia.installedInfo.id != info->cia.id ? "Yes" : "No");
|
||||
|
||||
float infoWidth;
|
||||
screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f);
|
||||
|
||||
float infoX = x1 + (x2 - x1 - infoWidth) / 2;
|
||||
float infoY = y1 + (y2 - y1) / 2 - 8;
|
||||
screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true);
|
||||
}
|
||||
|
||||
void task_draw_titledb_info_tdsx(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
|
||||
titledb_info* info = (titledb_info*) data;
|
||||
|
||||
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
|
||||
|
||||
char dbMtime[32];
|
||||
task_format_date(dbMtime, info->tdsx.mtime, sizeof(dbMtime));
|
||||
|
||||
char localMtime[32];
|
||||
task_format_date(localMtime, info->tdsx.installedInfo.mtime, sizeof(localMtime));
|
||||
|
||||
char infoText[512];
|
||||
|
||||
snprintf(infoText, sizeof(infoText),
|
||||
"Size: %.2f %s\n"
|
||||
"TitleDB Updated At: %s\n"
|
||||
"Local Updated At: %s\n"
|
||||
"TitleDB Version: %s\n"
|
||||
"Local Version: %s\n"
|
||||
"Update Available: %s",
|
||||
ui_get_display_size(info->tdsx.size), ui_get_display_size_units(info->tdsx.size),
|
||||
dbMtime,
|
||||
info->tdsx.installed ? localMtime : "Not Installed",
|
||||
info->tdsx.version,
|
||||
info->tdsx.installed ? info->tdsx.installedInfo.version : "Not Installed",
|
||||
info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id ? "Yes" : "No");
|
||||
|
||||
float infoWidth;
|
||||
screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f);
|
||||
|
||||
float infoX = x1 + (x2 - x1 - infoWidth) / 2;
|
||||
float infoY = y1 + (y2 - y1) / 2 - 8;
|
||||
screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true);
|
||||
|
@ -17,14 +17,10 @@ void task_draw_pending_title_info(ui_view* view, void* data, float x1, float y1,
|
||||
void task_draw_system_save_data_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
void task_draw_ticket_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
void task_draw_title_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
void task_draw_titledb_info(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
void task_draw_titledb_info_cia(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
void task_draw_titledb_info_tdsx(ui_view* view, void* data, float x1, float y1, float x2, float y2);
|
||||
|
||||
#include "listextsavedata.h"
|
||||
#include "listpendingtitles.h"
|
||||
#include "listsystemsavedata.h"
|
||||
#include "listtickets.h"
|
||||
#include "listtitledb.h"
|
||||
#include "listtitles.h"
|
||||
#include "listfiles.h"
|
@ -1,426 +0,0 @@
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <3ds.h>
|
||||
|
||||
#include "resources.h"
|
||||
#include "section.h"
|
||||
#include "action/action.h"
|
||||
#include "task/uitask.h"
|
||||
#include "../core/core.h"
|
||||
|
||||
static list_item install = {"Install", COLOR_TEXT, action_install_titledb};
|
||||
static list_item mark_updated = {"Mark Updated", COLOR_TEXT, action_mark_titledb_updated};
|
||||
|
||||
typedef struct {
|
||||
populate_titledb_data populateData;
|
||||
|
||||
bool showCIAs;
|
||||
bool show3DSXs;
|
||||
bool sortByName;
|
||||
bool sortByUpdate;
|
||||
bool sortByStatus;
|
||||
|
||||
bool populated;
|
||||
} titledb_data;
|
||||
|
||||
typedef struct {
|
||||
titledb_data* parent;
|
||||
linked_list* items;
|
||||
} titledb_options_data;
|
||||
|
||||
typedef struct {
|
||||
linked_list* items;
|
||||
list_item* selected;
|
||||
} titledb_entry_data;
|
||||
|
||||
typedef struct {
|
||||
linked_list* items;
|
||||
list_item* selected;
|
||||
bool cia;
|
||||
} titledb_action_data;
|
||||
|
||||
static void titledb_action_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
|
||||
titledb_action_data* actionData = (titledb_action_data*) data;
|
||||
|
||||
if(actionData->cia) {
|
||||
task_draw_titledb_info_cia(view, actionData->selected->data, x1, y1, x2, y2);
|
||||
} else {
|
||||
task_draw_titledb_info_tdsx(view, actionData->selected->data, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_action_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
|
||||
titledb_action_data* actionData = (titledb_action_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
ui_pop();
|
||||
list_destroy(view);
|
||||
|
||||
free(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
|
||||
void(*action)(linked_list*, list_item*, bool) = (void(*)(linked_list*, list_item*, bool)) selected->data;
|
||||
|
||||
ui_pop();
|
||||
list_destroy(view);
|
||||
|
||||
action(actionData->items, actionData->selected, actionData->cia);
|
||||
|
||||
free(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(linked_list_size(items) == 0) {
|
||||
linked_list_add(items, &install);
|
||||
linked_list_add(items, &mark_updated);
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_action_open(linked_list* items, list_item* selected, bool cia) {
|
||||
titledb_action_data* data = (titledb_action_data*) calloc(1, sizeof(titledb_action_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate TitleDB action data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->items = items;
|
||||
data->selected = selected;
|
||||
data->cia = cia;
|
||||
|
||||
list_display("TitleDB Action", "A: Select, B: Return", data, titledb_action_update, titledb_action_draw_top);
|
||||
}
|
||||
|
||||
static void titledb_entry_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
|
||||
titledb_entry_data* entryData = (titledb_entry_data*) data;
|
||||
|
||||
if(selected != NULL) {
|
||||
if(strncmp(selected->name, "CIA", sizeof(selected->name)) == 0) {
|
||||
task_draw_titledb_info_cia(view, entryData->selected->data, x1, y1, x2, y2);
|
||||
} else if(strncmp(selected->name, "3DSX", sizeof(selected->name)) == 0) {
|
||||
task_draw_titledb_info_tdsx(view, entryData->selected->data, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_entry_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
|
||||
titledb_entry_data* entryData = (titledb_entry_data*) data;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
ui_pop();
|
||||
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
free(linked_list_iter_next(&iter));
|
||||
linked_list_iter_remove(&iter);
|
||||
}
|
||||
|
||||
list_destroy(view);
|
||||
free(data);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(selected != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
|
||||
titledb_action_open(entryData->items, entryData->selected, (bool) selected->data);
|
||||
return;
|
||||
}
|
||||
|
||||
titledb_info* info = (titledb_info*) entryData->selected->data;
|
||||
if(linked_list_size(items) == 0) {
|
||||
if(info->cia.exists) {
|
||||
list_item* item = (list_item*) calloc(1, sizeof(list_item));
|
||||
if(item != NULL) {
|
||||
string_copy(item->name, "CIA", sizeof(item->name));
|
||||
item->data = (void*) true;
|
||||
item->color = info->cia.installed ? info->cia.installedInfo.id != info->cia.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
|
||||
|
||||
linked_list_add(items, item);
|
||||
}
|
||||
}
|
||||
|
||||
if(info->tdsx.exists) {
|
||||
list_item* item = (list_item*) calloc(1, sizeof(list_item));
|
||||
if(item != NULL) {
|
||||
string_copy(item->name, "3DSX", sizeof(item->name));
|
||||
item->data = (void*) false;
|
||||
item->color = info->tdsx.installed ? info->tdsx.installedInfo.id != info->tdsx.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
|
||||
|
||||
linked_list_add(items, item);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
list_item* item = (list_item*) linked_list_iter_next(&iter);
|
||||
|
||||
if((bool) item->data) {
|
||||
item->color = info->cia.installed ? info->cia.installedInfo.id != info->cia.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
|
||||
} else {
|
||||
item->color = info->tdsx.installed ? info->tdsx.installedInfo.id != info->tdsx.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_entry_open(linked_list* items, list_item* selected) {
|
||||
titledb_entry_data* data = (titledb_entry_data*) calloc(1, sizeof(titledb_entry_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate TitleDB entry data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->items = items;
|
||||
data->selected = selected;
|
||||
|
||||
list_display("TitleDB Entry", "A: Select, B: Return", data, titledb_entry_update, titledb_entry_draw_top);
|
||||
}
|
||||
|
||||
static int titledb_compare(void* data, const void* p1, const void* p2) {
|
||||
titledb_data* listData = (titledb_data*) data;
|
||||
|
||||
list_item* info1 = (list_item*) p1;
|
||||
list_item* info2 = (list_item*) p2;
|
||||
|
||||
titledb_info* data1 = (titledb_info*) info1->data;
|
||||
titledb_info* data2 = (titledb_info*) info2->data;
|
||||
|
||||
if(listData->sortByName) {
|
||||
return strncasecmp(info1->name, info2->name, sizeof(info1->name));
|
||||
} else if(listData->sortByUpdate) {
|
||||
return strncasecmp(data2->mtime, data1->mtime, sizeof(data2->mtime));
|
||||
} else if(listData->sortByStatus) {
|
||||
bool outdated1 = (data1->cia.installed && data1->cia.installedInfo.id != data1->cia.id)
|
||||
|| (data1->tdsx.installed && data1->tdsx.installedInfo.id != data1->tdsx.id);
|
||||
bool outdated2 = (data2->cia.installed && data2->cia.installedInfo.id != data2->cia.id)
|
||||
|| (data2->tdsx.installed && data2->tdsx.installedInfo.id != data2->tdsx.id);
|
||||
|
||||
if(outdated1 && !outdated2) {
|
||||
return -1;
|
||||
} else if(!outdated1 && outdated2) {
|
||||
return 1;
|
||||
} else {
|
||||
bool installed1 = data1->cia.installed || data1->tdsx.installed;
|
||||
bool installed2 = data2->cia.installed || data2->tdsx.installed;
|
||||
|
||||
if(installed1 && !installed2) {
|
||||
return -1;
|
||||
} else if(!installed1 && installed2) {
|
||||
return 1;
|
||||
} else {
|
||||
return strncasecmp(info1->name, info2->name, sizeof(info1->name));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_options_add_entry(linked_list* items, const char* name, bool* val) {
|
||||
list_item* item = (list_item*) calloc(1, sizeof(list_item));
|
||||
if(item != NULL) {
|
||||
snprintf(item->name, LIST_ITEM_NAME_MAX, "%s", name);
|
||||
item->color = *val ? COLOR_ENABLED : COLOR_DISABLED;
|
||||
item->data = val;
|
||||
|
||||
linked_list_add(items, item);
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_options_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
|
||||
titledb_options_data* optionsData = (titledb_options_data*) data;
|
||||
titledb_data* listData = optionsData->parent;
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
free(linked_list_iter_next(&iter));
|
||||
linked_list_iter_remove(&iter);
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
list_destroy(view);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
|
||||
bool* val = (bool*) selected->data;
|
||||
*val = !(*val);
|
||||
|
||||
if(val == &listData->sortByName || val == &listData->sortByUpdate || val == &listData->sortByStatus) {
|
||||
if(*val) {
|
||||
if(val == &listData->sortByName) {
|
||||
listData->sortByUpdate = false;
|
||||
listData->sortByStatus = false;
|
||||
} else if(val == &listData->sortByUpdate) {
|
||||
listData->sortByName = false;
|
||||
listData->sortByStatus = false;
|
||||
} else if(val == &listData->sortByStatus) {
|
||||
listData->sortByName = false;
|
||||
listData->sortByUpdate = false;
|
||||
}
|
||||
|
||||
linked_list_iter iter;
|
||||
linked_list_iterate(items, &iter);
|
||||
while(linked_list_iter_has_next(&iter)) {
|
||||
list_item* item = (list_item*) linked_list_iter_next(&iter);
|
||||
|
||||
item->color = *(bool*) item->data ? COLOR_ENABLED : COLOR_DISABLED;
|
||||
}
|
||||
} else {
|
||||
selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED;
|
||||
}
|
||||
|
||||
linked_list_sort(optionsData->items, listData, titledb_compare);
|
||||
} else {
|
||||
selected->color = *val ? COLOR_ENABLED : COLOR_DISABLED;
|
||||
|
||||
listData->populated = false;
|
||||
}
|
||||
}
|
||||
|
||||
if(linked_list_size(items) == 0) {
|
||||
titledb_options_add_entry(items, "Show CIAs", &listData->showCIAs);
|
||||
titledb_options_add_entry(items, "Show 3DSXs", &listData->show3DSXs);
|
||||
titledb_options_add_entry(items, "Sort by name", &listData->sortByName);
|
||||
titledb_options_add_entry(items, "Sort by update date", &listData->sortByUpdate);
|
||||
titledb_options_add_entry(items, "Sort by install status", &listData->sortByStatus);
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_options_open(titledb_data* parent, linked_list* items) {
|
||||
titledb_options_data* data = (titledb_options_data*) calloc(1, sizeof(titledb_options_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate TitleDB options data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->parent = parent;
|
||||
data->items = items;
|
||||
|
||||
list_display("Options", "A: Toggle, B: Return", data, titledb_options_update, NULL);
|
||||
}
|
||||
|
||||
static void titledb_draw_top(ui_view* view, void* data, float x1, float y1, float x2, float y2, list_item* selected) {
|
||||
titledb_data* listData = (titledb_data*) data;
|
||||
|
||||
if(!listData->populateData.itemsListed) {
|
||||
static const char* text = "Loading title list, please wait...\nNOTE: Cancelling may take up to 15 seconds.";
|
||||
|
||||
float textWidth;
|
||||
float textHeight;
|
||||
screen_get_string_size(&textWidth, &textHeight, text, 0.5f, 0.5f);
|
||||
screen_draw_string(text, x1 + (x2 - x1 - textWidth) / 2, y1 + (y2 - y1 - textHeight) / 2, 0.5f, 0.5f, COLOR_TEXT, true);
|
||||
} else if(selected != NULL && selected->data != NULL) {
|
||||
task_draw_titledb_info(view, selected->data, x1, y1, x2, y2);
|
||||
}
|
||||
}
|
||||
|
||||
static void titledb_update(ui_view* view, void* data, linked_list* items, list_item* selected, bool selectedTouched) {
|
||||
titledb_data* listData = (titledb_data*) data;
|
||||
|
||||
svcSignalEvent(listData->populateData.resumeEvent);
|
||||
|
||||
if(hidKeysDown() & KEY_B) {
|
||||
if(!listData->populateData.finished) {
|
||||
svcSignalEvent(listData->populateData.cancelEvent);
|
||||
while(!listData->populateData.finished) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
ui_pop();
|
||||
|
||||
task_clear_titledb(items);
|
||||
list_destroy(view);
|
||||
|
||||
free(listData);
|
||||
return;
|
||||
}
|
||||
|
||||
if(!listData->populated || (hidKeysDown() & KEY_X)) {
|
||||
if(!listData->populateData.finished) {
|
||||
svcSignalEvent(listData->populateData.cancelEvent);
|
||||
while(!listData->populateData.finished) {
|
||||
svcSleepThread(1000000);
|
||||
}
|
||||
}
|
||||
|
||||
listData->populateData.items = items;
|
||||
Result res = task_populate_titledb(&listData->populateData);
|
||||
if(R_FAILED(res)) {
|
||||
error_display_res(NULL, NULL, res, "Failed to initiate TitleDB list population.");
|
||||
}
|
||||
|
||||
listData->populated = true;
|
||||
}
|
||||
|
||||
if(listData->populateData.itemsListed) {
|
||||
if(hidKeysDown() & KEY_Y) {
|
||||
action_update_titledb(items, selected);
|
||||
return;
|
||||
}
|
||||
|
||||
if(hidKeysDown() & KEY_SELECT) {
|
||||
titledb_options_open(listData, items);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if(listData->populateData.finished && R_FAILED(listData->populateData.result)) {
|
||||
error_display_res(NULL, NULL, listData->populateData.result, "Failed to populate TitleDB list.");
|
||||
|
||||
listData->populateData.result = 0;
|
||||
}
|
||||
|
||||
if(selected != NULL && selected->data != NULL && (selectedTouched || (hidKeysDown() & KEY_A))) {
|
||||
svcClearEvent(listData->populateData.resumeEvent);
|
||||
|
||||
titledb_entry_open(items, selected);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
static bool titledb_filter(void* data, titledb_info* info) {
|
||||
titledb_data* listData = (titledb_data*) data;
|
||||
|
||||
return (info->cia.exists && listData->showCIAs) || (info->tdsx.exists && listData->show3DSXs);
|
||||
}
|
||||
|
||||
void titledb_open() {
|
||||
titledb_data* data = (titledb_data*) calloc(1, sizeof(titledb_data));
|
||||
if(data == NULL) {
|
||||
error_display(NULL, NULL, "Failed to allocate TitleDB data.");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
data->showCIAs = true;
|
||||
data->show3DSXs = true;
|
||||
data->sortByName = true;
|
||||
data->sortByUpdate = false;
|
||||
data->sortByStatus = false;
|
||||
|
||||
data->populateData.finished = true;
|
||||
|
||||
data->populateData.userData = data;
|
||||
data->populateData.filter = titledb_filter;
|
||||
data->populateData.compare = titledb_compare;
|
||||
|
||||
list_display("TitleDB.com", "A: Select, B: Return, X: Refresh, Y: Update All, Select: Options", data, titledb_update, titledb_draw_top);
|
||||
}
|
@ -14,15 +14,8 @@
|
||||
typedef struct {
|
||||
u32 id;
|
||||
bool cia;
|
||||
titledb_cache_entry data;
|
||||
} update_data;
|
||||
|
||||
static void update_finished_url(void* data, u32 index) {
|
||||
update_data* updateData = (update_data*) data;
|
||||
|
||||
task_populate_titledb_cache_set(updateData->id, updateData->cia, &updateData->data);
|
||||
}
|
||||
|
||||
static void update_finished_all(void* data) {
|
||||
free(data);
|
||||
}
|
||||
@ -39,7 +32,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true&only=id"
|
||||
"&only=cia.id&only=cia.version&only=cia.updated_at"
|
||||
"&only=tdsx.id&only=tdsx.version&only=tdsx.updated_at"
|
||||
"&_filters=%7B%22name%22%3A%20%22FBI%22%2C%20%22author%22%3A%20%22Steveice10%22%7D", &json, 16 * 1024))) {
|
||||
"&_filters=%7B%22id%22%3A%20%22138%22%7D", &json, 16 * 1024))) {
|
||||
const char* type = fs_get_3dsx_path() != NULL ? "tdsx" : "cia";
|
||||
|
||||
json_t* entry = NULL;
|
||||
@ -53,6 +46,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
updateData->id = (u32) json_integer_value(idJson);
|
||||
updateData->cia = fs_get_3dsx_path() != NULL;
|
||||
|
||||
u32 id = 0;
|
||||
u32 latestMajor = 0;
|
||||
u32 latestMinor = 0;
|
||||
u32 latestMicro = 0;
|
||||
@ -62,11 +56,9 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
if(json_is_object(obj)) {
|
||||
json_t* subIdJson = json_object_get(obj, "id");
|
||||
json_t* versionJson = json_object_get(obj, "version");
|
||||
json_t* updatedAtJson = json_object_get(obj, "updated_at");
|
||||
if(json_is_integer(subIdJson) && json_is_string(versionJson) && json_is_string(updatedAtJson)) {
|
||||
if(json_is_integer(subIdJson) && json_is_string(versionJson)) {
|
||||
u32 subId = (u32) json_integer_value(subIdJson);
|
||||
const char* version = json_string_value(versionJson);
|
||||
const char* updatedAt = json_string_value(updatedAtJson);
|
||||
|
||||
u32 major = 0;
|
||||
u32 minor = 0;
|
||||
@ -76,10 +68,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
if(major > latestMajor
|
||||
|| (major == latestMajor && minor > latestMinor)
|
||||
|| (major == latestMajor && minor == latestMinor && micro > latestMicro)) {
|
||||
updateData->data.id = subId;
|
||||
string_copy(updateData->data.mtime, updatedAt, sizeof(updateData->data.mtime));
|
||||
string_copy(updateData->data.version, version, sizeof(updateData->data.version));
|
||||
|
||||
id = subId;
|
||||
latestMajor = major;
|
||||
latestMinor = minor;
|
||||
latestMicro = micro;
|
||||
@ -91,7 +80,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
if(latestMajor > VERSION_MAJOR
|
||||
|| (latestMajor == VERSION_MAJOR && latestMinor > VERSION_MINOR)
|
||||
|| (latestMajor == VERSION_MAJOR && latestMinor == VERSION_MINOR && latestMicro > VERSION_MICRO)) {
|
||||
snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, updateData->data.id);
|
||||
snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, id);
|
||||
hasUpdate = true;
|
||||
}
|
||||
}
|
||||
@ -106,7 +95,7 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
|
||||
info_destroy(view);
|
||||
|
||||
if(hasUpdate) {
|
||||
action_install_url("Update FBI to the latest version?", updateURL, fs_get_3dsx_path(), updateData, update_finished_url, update_finished_all, NULL);
|
||||
action_install_url("Update FBI to the latest version?", updateURL, fs_get_3dsx_path(), updateData, NULL, update_finished_all, NULL);
|
||||
} else {
|
||||
if(R_FAILED(res)) {
|
||||
error_display_res(NULL, NULL, res, "Failed to check for update.");
|
||||
|
Loading…
x
Reference in New Issue
Block a user