Remove TitleDB feature, fix update query filter.

This commit is contained in:
Steveice10 2018-12-11 23:35:41 -08:00
parent 58c7dd0d17
commit aeb7b9056f
16 changed files with 9 additions and 1339 deletions

View File

@ -7,8 +7,5 @@ file=FF000000
directory=FF0000FF
enabled=FF00FF00
disabled=FF0000FF
titledbinstalled=FF00FF00
titledboutdated=FFFF0000
titledbnotinstalled=FF0000FF
ticketinuse=FF00FF00
ticketnotinuse=FF0000FF

View File

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

View File

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

View File

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

View File

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

View File

@ -155,7 +155,6 @@ void init() {
void cleanup() {
clipboard_clear();
task_populate_titledb_cache_unload();
task_exit();
ui_exit();

View File

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

View File

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

View File

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

View File

@ -15,5 +15,4 @@ void remoteinstall_open();
void systemsavedata_open();
void tickets_open();
void titles_open();
void titledb_open();
void update_open();

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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