Add version and update date to local TitleDB cache, display on screen.

This commit is contained in:
Steven Smith 2018-02-24 15:02:59 -08:00
parent 6ee8f666ac
commit 0f4031d256
10 changed files with 219 additions and 113 deletions

View File

@ -1,3 +1,4 @@
#include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h> #include <string.h>
@ -6,6 +7,7 @@
#include "error.h" #include "error.h"
#include "fs.h" #include "fs.h"
#include "linkedlist.h" #include "linkedlist.h"
#include "stringutil.h"
bool fs_is_dir(FS_Archive archive, const char* path) { bool fs_is_dir(FS_Archive archive, const char* path) {
Result res = 0; Result res = 0;
@ -171,6 +173,20 @@ void fs_set_3dsx_path(const char* path) {
} }
} }
int fs_make_3dsx_path(char* out, const char* name, size_t size) {
char filename[FILE_NAME_MAX];
string_escape_file_name(filename, name, sizeof(filename));
return snprintf(out, size, "/3ds/%s/%s.3dsx", filename, filename);
}
int fs_make_smdh_path(char* out, const char* name, size_t size) {
char filename[FILE_NAME_MAX];
string_escape_file_name(filename, name, sizeof(filename));
return snprintf(out, size, "/3ds/%s/%s.smdh", filename, filename);
}
FS_MediaType fs_get_title_destination(u64 titleId) { FS_MediaType fs_get_title_destination(u64 titleId) {
u16 platform = (u16) ((titleId >> 48) & 0xFFFF); u16 platform = (u16) ((titleId >> 48) & 0xFFFF);
u16 category = (u16) ((titleId >> 32) & 0xFFFF); u16 category = (u16) ((titleId >> 32) & 0xFFFF);

View File

@ -17,6 +17,9 @@ Result fs_close_archive(FS_Archive archive);
const char* fs_get_3dsx_path(); const char* fs_get_3dsx_path();
void fs_set_3dsx_path(const char* path); void fs_set_3dsx_path(const char* path);
int fs_make_3dsx_path(char* out, const char* name, size_t size);
int fs_make_smdh_path(char* out, const char* name, size_t size);
FS_MediaType fs_get_title_destination(u64 titleId); FS_MediaType fs_get_title_destination(u64 titleId);
bool fs_filter_cias(void* data, const char* name, u32 attributes); bool fs_filter_cias(void* data, const char* name, u32 attributes);

View File

@ -1,5 +1,6 @@
#include <stdio.h> #include <stdio.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#include <3ds.h> #include <3ds.h>
@ -27,7 +28,18 @@ static void action_install_titledb_finished_url(void* data, u32 index) {
list_item* item = installData->selected; list_item* item = installData->selected;
titledb_info* info = (titledb_info*) item->data; titledb_info* info = (titledb_info*) item->data;
task_populate_titledb_cache_installed(info->id, installData->cia ? info->cia.id : info->tdsx.id, installData->cia); titledb_cache_entry entry;
if(installData->cia) {
entry.id = info->cia.id;
strncpy(entry.updatedAt, info->cia.updatedAt, sizeof(entry.updatedAt));
strncpy(entry.version, info->cia.version, sizeof(entry.version));
} else {
entry.id = info->tdsx.id;
strncpy(entry.updatedAt, info->tdsx.updatedAt, sizeof(entry.updatedAt));
strncpy(entry.version, info->tdsx.version, sizeof(entry.version));
}
task_populate_titledb_cache_set(info->id, installData->cia, &entry);
task_populate_titledb_update_status(item); task_populate_titledb_update_status(item);
} }
@ -53,18 +65,20 @@ void action_install_titledb(linked_list* items, list_item* selected, bool cia) {
if(data->cia) { if(data->cia) {
snprintf(urls, sizeof(urls), "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id); snprintf(urls, sizeof(urls), "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id);
} else { } else {
char name[FILE_NAME_MAX]; char filePath[FILE_PATH_MAX];
string_escape_file_name(name, info->meta.shortDescription, sizeof(name)); fs_make_3dsx_path(filePath, info->meta.shortDescription, sizeof(filePath));
u32 urlsPos = 0; u32 urlsPos = 0;
u32 pathsPos = 0; u32 pathsPos = 0;
urlsPos += snprintf(urls + urlsPos, sizeof(urls) - urlsPos, "https://3ds.titledb.com/v1/tdsx/%lu/download\n", info->tdsx.id); 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, "/3ds/%s/%s.3dsx\n", name, name); pathsPos += snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "%s\n", filePath);
if(info->tdsx.smdh.exists) { 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(urls + urlsPos, sizeof(urls) - urlsPos, "https://3ds.titledb.com/v1/smdh/%lu/download\n", info->tdsx.smdh.id);
snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "/3ds/%s/%s.smdh\n", name, name); snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "%s\n", filePath);
} }
} }

View File

@ -33,7 +33,18 @@ static void action_update_titledb_finished_url(void* data, u32 index) {
list_item* item = updateData->items[index]; list_item* item = updateData->items[index];
titledb_info* info = (titledb_info*) item->data; titledb_info* info = (titledb_info*) item->data;
task_populate_titledb_cache_installed(info->id, updateData->cia[index] ? info->cia.id : info->tdsx.id, updateData->cia[index]); titledb_cache_entry entry;
if(updateData->cia[index]) {
entry.id = info->cia.id;
strncpy(entry.updatedAt, info->cia.updatedAt, sizeof(entry.updatedAt));
strncpy(entry.version, info->cia.version, sizeof(entry.version));
} else {
entry.id = info->tdsx.id;
strncpy(entry.updatedAt, info->tdsx.updatedAt, sizeof(entry.updatedAt));
strncpy(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); task_populate_titledb_update_status(item);
} }
@ -59,7 +70,7 @@ void action_update_titledb(linked_list* items, list_item* selected) {
list_item* item = linked_list_iter_next(&iter); list_item* item = linked_list_iter_next(&iter);
titledb_info* info = (titledb_info*) item->data; titledb_info* info = (titledb_info*) item->data;
if(info->cia.outdated) { if(info->cia.installed && info->cia.installedInfo.id != info->cia.id) {
urlsPos += snprintf(data->urls + urlsPos, INSTALL_URLS_MAX * DOWNLOAD_URL_MAX - urlsPos, urlsPos += snprintf(data->urls + urlsPos, INSTALL_URLS_MAX * DOWNLOAD_URL_MAX - urlsPos,
"https://3ds.titledb.com/v1/cia/%lu/download\n", "https://3ds.titledb.com/v1/cia/%lu/download\n",
info->cia.id); info->cia.id);
@ -72,20 +83,22 @@ void action_update_titledb(linked_list* items, list_item* selected) {
index++; index++;
} }
if(info->tdsx.outdated && (!info->tdsx.smdh.exists || index < INSTALL_URLS_MAX - 1)) { if(info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id && (!info->tdsx.smdh.exists || index < INSTALL_URLS_MAX - 1)) {
char name[FILE_NAME_MAX]; char filePath[FILE_PATH_MAX];
string_escape_file_name(name, info->meta.shortDescription, sizeof(name)); 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); 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, "/3ds/%s/%s.3dsx\n", name, name); pathsPos += snprintf(data->paths + pathsPos, INSTALL_URLS_MAX * FILE_PATH_MAX - pathsPos, "%s\n", filePath);
data->cia[index] = false; data->cia[index] = false;
data->items[index] = item; data->items[index] = item;
index++; index++;
if(info->tdsx.smdh.exists) { 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); 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, "/3ds/%s/%s.smdh\n", name, name); pathsPos += snprintf(data->paths + pathsPos, INSTALL_URLS_MAX * FILE_PATH_MAX - pathsPos, "%s\n", filePath);
data->cia[index] = false; data->cia[index] = false;
data->items[index] = item; data->items[index] = item;

View File

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

View File

@ -22,7 +22,7 @@
static json_t* installedApps = NULL; static json_t* installedApps = NULL;
static void task_populate_titledb_load_cache() { static void task_populate_titledb_cache_load() {
if(installedApps != NULL) { if(installedApps != NULL) {
json_decref(installedApps); json_decref(installedApps);
} }
@ -39,7 +39,7 @@ static void task_populate_titledb_load_cache() {
} }
} }
static void task_populate_titledb_save_cache() { static void task_populate_titledb_cache_save() {
if(json_is_object(installedApps)) { if(json_is_object(installedApps)) {
mkdir(TITLEDB_CACHE_DIR, 755); mkdir(TITLEDB_CACHE_DIR, 755);
@ -47,18 +47,18 @@ static void task_populate_titledb_save_cache() {
} }
} }
void task_populate_titledb_unload_cache() { void task_populate_titledb_cache_unload() {
if(json_is_object(installedApps)) { if(json_is_object(installedApps)) {
task_populate_titledb_save_cache(); task_populate_titledb_cache_save();
json_decref(installedApps); json_decref(installedApps);
installedApps = NULL; installedApps = NULL;
} }
} }
static json_t* task_populate_titledb_get_cache_entry(u32 id) { static json_t* task_populate_titledb_cache_get_base(u32 id, bool cia, bool create) {
if(!json_is_object(installedApps)) { if(!json_is_object(installedApps)) {
task_populate_titledb_load_cache(); task_populate_titledb_cache_load();
} }
if(json_is_object(installedApps)) { if(json_is_object(installedApps)) {
@ -67,26 +67,68 @@ static json_t* task_populate_titledb_get_cache_entry(u32 id) {
json_t* cache = json_object_get(installedApps, idString); json_t* cache = json_object_get(installedApps, idString);
if(!json_is_object(cache)) { if(!json_is_object(cache)) {
if(create) {
cache = json_object(); cache = json_object();
json_object_set(installedApps, idString, cache); json_object_set(installedApps, idString, cache);
} else {
cache = NULL;
}
} }
return cache; 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 { } 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; 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);
strncpy(entry->updatedAt, json_object_get_string(obj, "updated_at", "Unknown"), sizeof(entry->updatedAt));
strncpy(entry->version, json_object_get_string(obj, "version", "Unknown"), sizeof(entry->version));
return true;
}
} }
void task_populate_titledb_cache_installed(u32 id, u32 subId, bool cia) { return false;
json_t* cache = task_populate_titledb_get_cache_entry(id);
if(json_is_object(cache)) {
if(cia) {
json_object_set(cache, "cia_id", json_integer(subId));
} else {
json_object_set(cache, "tdsx_id", json_integer(subId));
} }
task_populate_titledb_save_cache(); 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, "updated_at", json_string(entry->updatedAt));
json_object_set(obj, "version", json_string(entry->version));
task_populate_titledb_cache_save();
} }
} }
@ -94,22 +136,17 @@ void task_populate_titledb_update_status(list_item* item) {
titledb_info* info = (titledb_info*) item->data; titledb_info* info = (titledb_info*) item->data;
info->cia.installed = false; info->cia.installed = false;
info->cia.outdated = false;
info->tdsx.installed = false; info->tdsx.installed = false;
info->tdsx.outdated = false;
if(info->cia.exists) { if(info->cia.exists) {
AM_TitleEntry entry; AM_TitleEntry entry;
info->cia.installed = R_SUCCEEDED(AM_GetTitleInfo(fs_get_title_destination(info->cia.titleId), 1, &info->cia.titleId, &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) { if(info->tdsx.exists) {
char name[FILE_NAME_MAX];
string_escape_file_name(name, info->meta.shortDescription, sizeof(name));
char path3dsx[FILE_PATH_MAX]; char path3dsx[FILE_PATH_MAX];
snprintf(path3dsx, sizeof(path3dsx), "/3ds/%s/%s.3dsx", name, name); fs_make_3dsx_path(path3dsx, info->meta.shortDescription, sizeof(path3dsx));
FS_Path* fsPath = fs_make_path_utf8(path3dsx); FS_Path* fsPath = fs_make_path_utf8(path3dsx);
if(fsPath != NULL) { if(fsPath != NULL) {
@ -117,37 +154,14 @@ void task_populate_titledb_update_status(list_item* item) {
if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) { if(R_SUCCEEDED(FSUSER_OpenFileDirectly(&handle, ARCHIVE_SDMC, fsMakePath(PATH_EMPTY, ""), *fsPath, FS_OPEN_READ, 0))) {
FSFILE_Close(handle); FSFILE_Close(handle);
info->tdsx.installed = true; info->tdsx.installed = task_populate_titledb_cache_get(info->id, false, &info->tdsx.installedInfo);
} }
fs_free_path_utf8(fsPath); fs_free_path_utf8(fsPath);
} }
} }
if((info->cia.exists && info->cia.installed) || (info->tdsx.exists && info->tdsx.installed)) { if((info->cia.installed && info->cia.installedInfo.id != info->cia.id) || (info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id)) {
json_t* cache = task_populate_titledb_get_cache_entry(info->id);
if(json_is_object(cache)) {
json_t* ciaId = json_object_get(cache, "cia_id");
json_t* tdsxId = json_object_get(cache, "tdsx_id");
info->cia.installed = info->cia.installed && json_is_integer(ciaId);
info->cia.outdated = info->cia.installed && json_integer_value(ciaId) < info->cia.id;
info->tdsx.installed = info->tdsx.installed && json_is_integer(tdsxId);
info->tdsx.outdated = info->tdsx.installed && json_integer_value(tdsxId) < info->tdsx.id;
} else {
// If no cache entry exists, consider this entry as not installed.
// Assuming an entry is outdated can cause issues with multiple entries that have the same name/title ID.
info->cia.installed = false;
info->cia.outdated = false;
info->tdsx.installed = false;
info->tdsx.outdated = false;
}
}
if(info->cia.outdated || info->tdsx.outdated) {
item->color = COLOR_TITLEDB_OUTDATED; item->color = COLOR_TITLEDB_OUTDATED;
} else if(info->cia.installed || info->tdsx.installed) { } else if(info->cia.installed || info->tdsx.installed) {
item->color = COLOR_TITLEDB_INSTALLED; item->color = COLOR_TITLEDB_INSTALLED;
@ -156,6 +170,21 @@ void task_populate_titledb_update_status(list_item* item) {
} }
} }
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) { static void task_populate_titledb_thread(void* arg) {
populate_titledb_data* data = (populate_titledb_data*) arg; populate_titledb_data* data = (populate_titledb_data*) arg;
@ -185,7 +214,7 @@ static void task_populate_titledb_thread(void* arg) {
if(titledbInfo != NULL) { if(titledbInfo != NULL) {
titledbInfo->id = (u32) json_object_get_integer(entry, "id", 0); titledbInfo->id = (u32) json_object_get_integer(entry, "id", 0);
strncpy(titledbInfo->category, json_object_get_string(entry, "category", "Unknown"), sizeof(titledbInfo->category)); strncpy(titledbInfo->category, json_object_get_string(entry, "category", "Unknown"), sizeof(titledbInfo->category));
strncpy(titledbInfo->updatedAt, json_object_get_string(entry, "updated_at", ""), sizeof(titledbInfo->updatedAt)); strncpy(titledbInfo->updatedAt, json_object_get_string(entry, "updated_at", "Unknown"), sizeof(titledbInfo->updatedAt));
strncpy(titledbInfo->meta.shortDescription, json_object_get_string(entry, "name", ""), sizeof(titledbInfo->meta.shortDescription)); strncpy(titledbInfo->meta.shortDescription, json_object_get_string(entry, "name", ""), sizeof(titledbInfo->meta.shortDescription));
strncpy(titledbInfo->meta.publisher, json_object_get_string(entry, "author", ""), sizeof(titledbInfo->meta.publisher)); strncpy(titledbInfo->meta.publisher, json_object_get_string(entry, "author", ""), sizeof(titledbInfo->meta.publisher));
@ -207,8 +236,8 @@ static void task_populate_titledb_thread(void* arg) {
for(u32 j = 0; j < json_array_size(cias); j++) { for(u32 j = 0; j < json_array_size(cias); j++) {
json_t* cia = json_array_get(cias, j); json_t* cia = json_array_get(cias, j);
if(json_is_object(cia)) { if(json_is_object(cia)) {
const char* updatedAt = json_object_get_string(cia, "updated_at", ""); const char* updatedAt = json_object_get_string(cia, "updated_at", "Unknown");
if(!titledbInfo->cia.exists || strncmp(updatedAt, titledbInfo->cia.updatedAt, sizeof(titledbInfo->cia.updatedAt)) >= 0) { if(!titledbInfo->cia.exists || task_populate_titledb_compare_dates(updatedAt, titledbInfo->cia.updatedAt, sizeof(titledbInfo->cia.updatedAt)) >= 0) {
titledbInfo->cia.exists = true; titledbInfo->cia.exists = true;
titledbInfo->cia.id = (u32) json_object_get_integer(cia, "id", 0); titledbInfo->cia.id = (u32) json_object_get_integer(cia, "id", 0);
@ -226,8 +255,8 @@ static void task_populate_titledb_thread(void* arg) {
for(u32 j = 0; j < json_array_size(tdsxs); j++) { for(u32 j = 0; j < json_array_size(tdsxs); j++) {
json_t* tdsx = json_array_get(tdsxs, j); json_t* tdsx = json_array_get(tdsxs, j);
if(json_is_object(tdsx)) { if(json_is_object(tdsx)) {
const char* updatedAt = json_object_get_string(tdsx, "updated_at", ""); const char* updatedAt = json_object_get_string(tdsx, "updated_at", "Unknown");
if(!titledbInfo->tdsx.exists || strncmp(updatedAt, titledbInfo->tdsx.updatedAt, sizeof(titledbInfo->tdsx.updatedAt)) >= 0) { if(!titledbInfo->tdsx.exists || task_populate_titledb_compare_dates(updatedAt, titledbInfo->tdsx.updatedAt, sizeof(titledbInfo->tdsx.updatedAt)) >= 0) {
titledbInfo->tdsx.exists = true; titledbInfo->tdsx.exists = true;
titledbInfo->tdsx.id = (u32) json_object_get_integer(tdsx, "id", 0); titledbInfo->tdsx.id = (u32) json_object_get_integer(tdsx, "id", 0);

View File

@ -3,6 +3,12 @@
typedef struct linked_list_s linked_list; typedef struct linked_list_s linked_list;
typedef struct list_item_s list_item; typedef struct list_item_s list_item;
typedef struct titledb_cache_entry_s {
u32 id;
char updatedAt[32];
char version[32];
} titledb_cache_entry;
typedef struct titledb_cia_info_s { typedef struct titledb_cia_info_s {
bool exists; bool exists;
@ -13,7 +19,7 @@ typedef struct titledb_cia_info_s {
u64 titleId; u64 titleId;
bool installed; bool installed;
bool outdated; titledb_cache_entry installedInfo;
} titledb_cia_info; } titledb_cia_info;
typedef struct titledb_smdh_info_s { typedef struct titledb_smdh_info_s {
@ -32,7 +38,7 @@ typedef struct titledb_tdsx_info_s {
titledb_smdh_info smdh; titledb_smdh_info smdh;
bool installed; bool installed;
bool outdated; titledb_cache_entry installedInfo;
} titledb_tdsx_info; } titledb_tdsx_info;
typedef struct titledb_info_s { typedef struct titledb_info_s {
@ -60,8 +66,8 @@ typedef struct populate_titledb_data_s {
Handle resumeEvent; Handle resumeEvent;
} populate_titledb_data; } populate_titledb_data;
void task_populate_titledb_unload_cache(); void task_populate_titledb_cache_unload();
void task_populate_titledb_cache_installed(u32 id, u32 subId, bool cia); 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_populate_titledb_update_status(list_item* item);
void task_free_titledb(list_item* item); void task_free_titledb(list_item* item);

View File

@ -262,15 +262,25 @@ void task_draw_title_info(ui_view* view, void* data, float x1, float y1, float x
screen_draw_string(infoText, infoX, infoY, 0.5f, 0.5f, COLOR_TEXT, true); 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) {
strncpy(out, date, size);
} else {
char updatedDate[32] = "";
char updatedTime[32] = "";
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) { void task_draw_titledb_info(ui_view* view, void* data, float x1, float y1, float x2, float y2) {
titledb_info* info = (titledb_info*) data; titledb_info* info = (titledb_info*) data;
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
char updatedDate[32] = ""; char updatedAt[32];
char updatedTime[32] = ""; task_format_date(updatedAt, info->updatedAt, sizeof(updatedAt));
sscanf(info->updatedAt, "%31[^T]T%31[^Z]Z", updatedDate, updatedTime);
char infoText[1024]; char infoText[1024];
@ -278,12 +288,12 @@ void task_draw_titledb_info(ui_view* view, void* data, float x1, float y1, float
"%s\n" "%s\n"
"\n" "\n"
"Category: %s\n" "Category: %s\n"
"Updated At: %s %s\n" "Updated At: %s\n"
"Update Available: %s", "Update Available: %s",
info->headline, info->headline,
info->category, info->category,
updatedDate, updatedTime, updatedAt,
info->cia.outdated || info->tdsx.outdated ? "Yes" : "No"); (info->cia.installed && info->cia.installedInfo.id != info->cia.id) || (info->tdsx.installed && info->tdsx.installedInfo.id != info->tdsx.id) ? "Yes" : "No");
float infoWidth; float infoWidth;
screen_get_string_size_wrap(&infoWidth, NULL, infoText, 0.5f, 0.5f, x2 - x1 - 10); screen_get_string_size_wrap(&infoWidth, NULL, infoText, 0.5f, 0.5f, x2 - x1 - 10);
@ -298,24 +308,29 @@ void task_draw_titledb_info_cia(ui_view* view, void* data, float x1, float y1, f
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
char updatedDate[32] = ""; char dbUpdatedAt[32];
char updatedTime[32] = ""; task_format_date(dbUpdatedAt, info->cia.updatedAt, sizeof(dbUpdatedAt));
sscanf(info->cia.updatedAt, "%31[^T]T%31[^Z]Z", updatedDate, updatedTime); char localUpdatedAt[32];
task_format_date(localUpdatedAt, info->cia.installedInfo.updatedAt, sizeof(localUpdatedAt));
char infoText[512]; char infoText[512];
snprintf(infoText, sizeof(infoText), snprintf(infoText, sizeof(infoText),
"Title ID: %016llX\n" "Title ID: %016llX\n"
"TitleDB Version: %s\n"
"Size: %.2f %s\n" "Size: %.2f %s\n"
"Updated At: %s %s\n" "TitleDB Updated At: %s\n"
"Local Updated At: %s\n"
"TitleDB Version: %s\n"
"Local Version: %s\n"
"Update Available: %s", "Update Available: %s",
info->cia.titleId, info->cia.titleId,
info->cia.version,
ui_get_display_size(info->cia.size), ui_get_display_size_units(info->cia.size), ui_get_display_size(info->cia.size), ui_get_display_size_units(info->cia.size),
updatedDate, updatedTime, dbUpdatedAt,
info->cia.outdated ? "Yes" : "No"); info->cia.installed ? localUpdatedAt : "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; float infoWidth;
screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f);
@ -330,22 +345,27 @@ void task_draw_titledb_info_tdsx(ui_view* view, void* data, float x1, float y1,
task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); task_draw_meta_info(view, &info->meta, x1, y1, x2, y2);
char updatedDate[32] = ""; char dbUpdatedAt[32];
char updatedTime[32] = ""; task_format_date(dbUpdatedAt, info->tdsx.updatedAt, sizeof(dbUpdatedAt));
sscanf(info->tdsx.updatedAt, "%31[^T]T%31[^Z]Z", updatedDate, updatedTime); char localUpdatedAt[32];
task_format_date(localUpdatedAt, info->tdsx.installedInfo.updatedAt, sizeof(localUpdatedAt));
char infoText[512]; char infoText[512];
snprintf(infoText, sizeof(infoText), snprintf(infoText, sizeof(infoText),
"TitleDB Version: %s\n"
"Size: %.2f %s\n" "Size: %.2f %s\n"
"Updated At: %s %s\n" "TitleDB Updated At: %s\n"
"Local Updated At: %s\n"
"TitleDB Version: %s\n"
"Local Version: %s\n"
"Update Available: %s", "Update Available: %s",
info->tdsx.version,
ui_get_display_size(info->tdsx.size), ui_get_display_size_units(info->tdsx.size), ui_get_display_size(info->tdsx.size), ui_get_display_size_units(info->tdsx.size),
updatedDate, updatedTime, dbUpdatedAt,
info->tdsx.outdated ? "Yes" : "No"); info->tdsx.installed ? localUpdatedAt : "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; float infoWidth;
screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f);

View File

@ -138,7 +138,7 @@ static void titledb_entry_update(ui_view* view, void* data, linked_list* items,
if(item != NULL) { if(item != NULL) {
strncpy(item->name, "CIA", sizeof(item->name)); strncpy(item->name, "CIA", sizeof(item->name));
item->data = (void*) true; item->data = (void*) true;
item->color = info->cia.installed ? info->cia.outdated ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED; 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); linked_list_add(items, item);
} }
@ -149,7 +149,7 @@ static void titledb_entry_update(ui_view* view, void* data, linked_list* items,
if(item != NULL) { if(item != NULL) {
strncpy(item->name, "3DSX", sizeof(item->name)); strncpy(item->name, "3DSX", sizeof(item->name));
item->data = (void*) false; item->data = (void*) false;
item->color = info->tdsx.installed ? info->tdsx.outdated ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED; 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); linked_list_add(items, item);
} }
@ -162,9 +162,9 @@ static void titledb_entry_update(ui_view* view, void* data, linked_list* items,
list_item* item = (list_item*) linked_list_iter_next(&iter); list_item* item = (list_item*) linked_list_iter_next(&iter);
if((bool) item->data) { if((bool) item->data) {
item->color = info->cia.installed ? info->cia.outdated ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED; item->color = info->cia.installed ? info->cia.installedInfo.id != info->cia.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
} else { } else {
item->color = info->tdsx.installed ? info->tdsx.outdated ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED; item->color = info->tdsx.installed ? info->tdsx.installedInfo.id != info->tdsx.id ? COLOR_TITLEDB_OUTDATED : COLOR_TITLEDB_INSTALLED : COLOR_TITLEDB_NOT_INSTALLED;
} }
} }
} }

View File

@ -13,14 +13,14 @@
typedef struct { typedef struct {
u32 id; u32 id;
u32 subId;
bool cia; bool cia;
titledb_cache_entry data;
} update_data; } update_data;
static void update_finished_url(void* data, u32 index) { static void update_finished_url(void* data, u32 index) {
update_data* updateData = (update_data*) data; update_data* updateData = (update_data*) data;
task_populate_titledb_cache_installed(updateData->id, updateData->subId, updateData->cia); task_populate_titledb_cache_set(updateData->id, updateData->cia, &updateData->data);
} }
static void update_finished_all(void* data) { static void update_finished_all(void* data) {
@ -36,7 +36,10 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
Result res = 0; Result res = 0;
json_t* json = NULL; json_t* json = NULL;
if(R_SUCCEEDED(res = http_download_json("https://api.titledb.com/v1/entry?nested=true&only=id&only=cia.id&only=cia.version&only=tdsx.id&only=tdsx.version&_filters=%7B%22name%22%3A%20%22FBI%22%7D", &json, 16 * 1024))) { 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%7D", &json, 16 * 1024))) {
const char* type = fs_get_3dsx_path() != NULL ? "tdsx" : "cia"; const char* type = fs_get_3dsx_path() != NULL ? "tdsx" : "cia";
json_t* entry = NULL; json_t* entry = NULL;
@ -48,8 +51,8 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
&& json_is_array(objs = json_object_get(entry, type))) { && json_is_array(objs = json_object_get(entry, type))) {
if(json_array_size(json) > 0) { if(json_array_size(json) > 0) {
updateData->id = (u32) json_integer_value(idJson); updateData->id = (u32) json_integer_value(idJson);
updateData->cia = fs_get_3dsx_path() != NULL;
u32 latestSubId = 0;
u32 latestMajor = 0; u32 latestMajor = 0;
u32 latestMinor = 0; u32 latestMinor = 0;
u32 latestMicro = 0; u32 latestMicro = 0;
@ -59,9 +62,11 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
if(json_is_object(obj)) { if(json_is_object(obj)) {
json_t* subIdJson = json_object_get(obj, "id"); json_t* subIdJson = json_object_get(obj, "id");
json_t* versionJson = json_object_get(obj, "version"); json_t* versionJson = json_object_get(obj, "version");
if(json_is_integer(subIdJson) && json_is_string(versionJson)) { json_t* updatedAtJson = json_object_get(obj, "updated_at");
if(json_is_integer(subIdJson) && json_is_string(versionJson) && json_is_string(updatedAtJson)) {
u32 subId = (u32) json_integer_value(subIdJson); u32 subId = (u32) json_integer_value(subIdJson);
const char* version = json_string_value(versionJson); const char* version = json_string_value(versionJson);
const char* updatedAt = json_string_value(updatedAtJson);
u32 major = 0; u32 major = 0;
u32 minor = 0; u32 minor = 0;
@ -71,7 +76,10 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
if(major > latestMajor if(major > latestMajor
|| (major == latestMajor && minor > latestMinor) || (major == latestMajor && minor > latestMinor)
|| (major == latestMajor && minor == latestMinor && micro > latestMicro)) { || (major == latestMajor && minor == latestMinor && micro > latestMicro)) {
latestSubId = subId; updateData->data.id = subId;
strncpy(updateData->data.updatedAt, updatedAt, sizeof(updateData->data.updatedAt));
strncpy(updateData->data.version, version, sizeof(updateData->data.version));
latestMajor = major; latestMajor = major;
latestMinor = minor; latestMinor = minor;
latestMicro = micro; latestMicro = micro;
@ -80,13 +88,10 @@ static void update_check_update(ui_view* view, void* data, float* progress, char
} }
} }
updateData->subId = latestSubId;
updateData->cia = fs_get_3dsx_path() != NULL;
if(latestMajor > VERSION_MAJOR if(latestMajor > VERSION_MAJOR
|| (latestMajor == VERSION_MAJOR && latestMinor > VERSION_MINOR) || (latestMajor == VERSION_MAJOR && latestMinor > VERSION_MINOR)
|| (latestMajor == VERSION_MAJOR && latestMinor == VERSION_MINOR && latestMicro > VERSION_MICRO)) { || (latestMajor == VERSION_MAJOR && latestMinor == VERSION_MINOR && latestMicro > VERSION_MICRO)) {
snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, latestSubId); snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, updateData->data.id);
hasUpdate = true; hasUpdate = true;
} }
} }