diff --git a/source/core/fs.c b/source/core/fs.c index 9a74f60..7e697c9 100644 --- a/source/core/fs.c +++ b/source/core/fs.c @@ -1,3 +1,4 @@ +#include #include #include @@ -6,6 +7,7 @@ #include "error.h" #include "fs.h" #include "linkedlist.h" +#include "stringutil.h" bool fs_is_dir(FS_Archive archive, const char* path) { 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) { u16 platform = (u16) ((titleId >> 48) & 0xFFFF); u16 category = (u16) ((titleId >> 32) & 0xFFFF); diff --git a/source/core/fs.h b/source/core/fs.h index 74aff4a..4ffb835 100644 --- a/source/core/fs.h +++ b/source/core/fs.h @@ -17,6 +17,9 @@ Result fs_close_archive(FS_Archive archive); const char* fs_get_3dsx_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); bool fs_filter_cias(void* data, const char* name, u32 attributes); diff --git a/source/fbi/action/installtitledb.c b/source/fbi/action/installtitledb.c index b2b86d7..f8cff56 100644 --- a/source/fbi/action/installtitledb.c +++ b/source/fbi/action/installtitledb.c @@ -1,5 +1,6 @@ #include #include +#include #include <3ds.h> @@ -27,7 +28,18 @@ static void action_install_titledb_finished_url(void* data, u32 index) { list_item* item = installData->selected; 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); } @@ -53,18 +65,20 @@ void action_install_titledb(linked_list* items, list_item* selected, bool cia) { if(data->cia) { snprintf(urls, sizeof(urls), "https://3ds.titledb.com/v1/cia/%lu/download", info->cia.id); } else { - char name[FILE_NAME_MAX]; - string_escape_file_name(name, info->meta.shortDescription, sizeof(name)); + 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, "/3ds/%s/%s.3dsx\n", name, name); + 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, "/3ds/%s/%s.smdh\n", name, name); + snprintf(paths + pathsPos, sizeof(paths) - pathsPos, "%s\n", filePath); } } diff --git a/source/fbi/action/updatetitledb.c b/source/fbi/action/updatetitledb.c index 98b8758..78580ed 100644 --- a/source/fbi/action/updatetitledb.c +++ b/source/fbi/action/updatetitledb.c @@ -33,7 +33,18 @@ static void action_update_titledb_finished_url(void* data, u32 index) { list_item* item = updateData->items[index]; 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); } @@ -59,7 +70,7 @@ void action_update_titledb(linked_list* items, list_item* selected) { list_item* item = linked_list_iter_next(&iter); 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, "https://3ds.titledb.com/v1/cia/%lu/download\n", info->cia.id); @@ -72,20 +83,22 @@ void action_update_titledb(linked_list* items, list_item* selected) { index++; } - if(info->tdsx.outdated && (!info->tdsx.smdh.exists || index < INSTALL_URLS_MAX - 1)) { - char name[FILE_NAME_MAX]; - string_escape_file_name(name, info->meta.shortDescription, sizeof(name)); + 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, "/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->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, "/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->items[index] = item; diff --git a/source/fbi/main.c b/source/fbi/main.c index 8c7ab70..7c5e6f8 100644 --- a/source/fbi/main.c +++ b/source/fbi/main.c @@ -155,7 +155,7 @@ void init() { void cleanup() { clipboard_clear(); - task_populate_titledb_unload_cache(); + task_populate_titledb_cache_unload(); task_exit(); ui_exit(); diff --git a/source/fbi/task/listtitledb.c b/source/fbi/task/listtitledb.c index c76c360..3fb94cb 100644 --- a/source/fbi/task/listtitledb.c +++ b/source/fbi/task/listtitledb.c @@ -22,7 +22,7 @@ static json_t* installedApps = NULL; -static void task_populate_titledb_load_cache() { +static void task_populate_titledb_cache_load() { if(installedApps != NULL) { 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)) { 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)) { - task_populate_titledb_save_cache(); + task_populate_titledb_cache_save(); json_decref(installedApps); 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)) { - task_populate_titledb_load_cache(); + task_populate_titledb_cache_load(); } 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); if(!json_is_object(cache)) { - cache = json_object(); - json_object_set(installedApps, idString, cache); + if(create) { + cache = json_object(); + json_object_set(installedApps, idString, cache); + } else { + cache = NULL; + } } - return cache; - } else { - return 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; } -void task_populate_titledb_cache_installed(u32 id, u32 subId, bool cia) { - 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)); - } +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)); - task_populate_titledb_save_cache(); + 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, "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; info->cia.installed = false; - info->cia.outdated = false; - info->tdsx.installed = false; - info->tdsx.outdated = 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)); + 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 name[FILE_NAME_MAX]; - string_escape_file_name(name, info->meta.shortDescription, sizeof(name)); - 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); 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))) { 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); } } - if((info->cia.exists && info->cia.installed) || (info->tdsx.exists && info->tdsx.installed)) { - 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) { + 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; @@ -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) { populate_titledb_data* data = (populate_titledb_data*) arg; @@ -185,7 +214,7 @@ static void task_populate_titledb_thread(void* arg) { if(titledbInfo != NULL) { 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->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.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++) { json_t* cia = json_array_get(cias, j); if(json_is_object(cia)) { - const char* updatedAt = json_object_get_string(cia, "updated_at", ""); - if(!titledbInfo->cia.exists || strncmp(updatedAt, titledbInfo->cia.updatedAt, sizeof(titledbInfo->cia.updatedAt)) >= 0) { + const char* updatedAt = json_object_get_string(cia, "updated_at", "Unknown"); + if(!titledbInfo->cia.exists || task_populate_titledb_compare_dates(updatedAt, titledbInfo->cia.updatedAt, sizeof(titledbInfo->cia.updatedAt)) >= 0) { titledbInfo->cia.exists = true; 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++) { json_t* tdsx = json_array_get(tdsxs, j); if(json_is_object(tdsx)) { - const char* updatedAt = json_object_get_string(tdsx, "updated_at", ""); - if(!titledbInfo->tdsx.exists || strncmp(updatedAt, titledbInfo->tdsx.updatedAt, sizeof(titledbInfo->tdsx.updatedAt)) >= 0) { + const char* updatedAt = json_object_get_string(tdsx, "updated_at", "Unknown"); + if(!titledbInfo->tdsx.exists || task_populate_titledb_compare_dates(updatedAt, titledbInfo->tdsx.updatedAt, sizeof(titledbInfo->tdsx.updatedAt)) >= 0) { titledbInfo->tdsx.exists = true; titledbInfo->tdsx.id = (u32) json_object_get_integer(tdsx, "id", 0); diff --git a/source/fbi/task/listtitledb.h b/source/fbi/task/listtitledb.h index f463b07..e39d2ba 100644 --- a/source/fbi/task/listtitledb.h +++ b/source/fbi/task/listtitledb.h @@ -3,6 +3,12 @@ typedef struct linked_list_s linked_list; 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 { bool exists; @@ -13,7 +19,7 @@ typedef struct titledb_cia_info_s { u64 titleId; bool installed; - bool outdated; + titledb_cache_entry installedInfo; } titledb_cia_info; typedef struct titledb_smdh_info_s { @@ -32,7 +38,7 @@ typedef struct titledb_tdsx_info_s { titledb_smdh_info smdh; bool installed; - bool outdated; + titledb_cache_entry installedInfo; } titledb_tdsx_info; typedef struct titledb_info_s { @@ -60,8 +66,8 @@ typedef struct populate_titledb_data_s { Handle resumeEvent; } populate_titledb_data; -void task_populate_titledb_unload_cache(); -void task_populate_titledb_cache_installed(u32 id, u32 subId, bool cia); +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); diff --git a/source/fbi/task/uitask.c b/source/fbi/task/uitask.c index dc85f56..a7cc568 100644 --- a/source/fbi/task/uitask.c +++ b/source/fbi/task/uitask.c @@ -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); } +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) { titledb_info* info = (titledb_info*) data; task_draw_meta_info(view, &info->meta, x1, y1, x2, y2); - char updatedDate[32] = ""; - char updatedTime[32] = ""; - - sscanf(info->updatedAt, "%31[^T]T%31[^Z]Z", updatedDate, updatedTime); + char updatedAt[32]; + task_format_date(updatedAt, info->updatedAt, sizeof(updatedAt)); char infoText[1024]; @@ -278,12 +288,12 @@ void task_draw_titledb_info(ui_view* view, void* data, float x1, float y1, float "%s\n" "\n" "Category: %s\n" - "Updated At: %s %s\n" + "Updated At: %s\n" "Update Available: %s", info->headline, info->category, - updatedDate, updatedTime, - info->cia.outdated || info->tdsx.outdated ? "Yes" : "No"); + updatedAt, + (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); @@ -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); - char updatedDate[32] = ""; - char updatedTime[32] = ""; + char dbUpdatedAt[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]; snprintf(infoText, sizeof(infoText), "Title ID: %016llX\n" - "TitleDB Version: %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", info->cia.titleId, - info->cia.version, ui_get_display_size(info->cia.size), ui_get_display_size_units(info->cia.size), - updatedDate, updatedTime, - info->cia.outdated ? "Yes" : "No"); + dbUpdatedAt, + 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; 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); - char updatedDate[32] = ""; - char updatedTime[32] = ""; + char dbUpdatedAt[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]; snprintf(infoText, sizeof(infoText), - "TitleDB Version: %s\n" - "Size: %.2f %s\n" - "Updated At: %s %s\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->tdsx.version, ui_get_display_size(info->tdsx.size), ui_get_display_size_units(info->tdsx.size), - updatedDate, updatedTime, - info->tdsx.outdated ? "Yes" : "No"); + dbUpdatedAt, + 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; screen_get_string_size(&infoWidth, NULL, infoText, 0.5f, 0.5f); diff --git a/source/fbi/titledb.c b/source/fbi/titledb.c index c471550..44e6815 100644 --- a/source/fbi/titledb.c +++ b/source/fbi/titledb.c @@ -138,7 +138,7 @@ static void titledb_entry_update(ui_view* view, void* data, linked_list* items, if(item != NULL) { strncpy(item->name, "CIA", sizeof(item->name)); 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); } @@ -149,7 +149,7 @@ static void titledb_entry_update(ui_view* view, void* data, linked_list* items, if(item != NULL) { strncpy(item->name, "3DSX", sizeof(item->name)); 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); } @@ -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); 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 { - 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; } } } diff --git a/source/fbi/update.c b/source/fbi/update.c index 17ffda1..b044ff3 100644 --- a/source/fbi/update.c +++ b/source/fbi/update.c @@ -13,14 +13,14 @@ typedef struct { u32 id; - u32 subId; 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_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) { @@ -36,7 +36,10 @@ static void update_check_update(ui_view* view, void* data, float* progress, char Result res = 0; 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"; 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))) { if(json_array_size(json) > 0) { updateData->id = (u32) json_integer_value(idJson); + updateData->cia = fs_get_3dsx_path() != NULL; - u32 latestSubId = 0; u32 latestMajor = 0; u32 latestMinor = 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)) { json_t* subIdJson = json_object_get(obj, "id"); 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); const char* version = json_string_value(versionJson); + const char* updatedAt = json_string_value(updatedAtJson); u32 major = 0; u32 minor = 0; @@ -71,7 +76,10 @@ 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)) { - 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; latestMinor = minor; 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 || (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, latestSubId); + snprintf(updateURL, DOWNLOAD_URL_MAX, "https://3ds.titledb.com/v1/%s/%lu/download", type, updateData->data.id); hasUpdate = true; } }