#define AL_LOG_SECTION "db" #include #include "db.h" #include "file_ext.h" #define REAL_OR_INT(v) \ json_typeof(v) == JSON_REAL ? json_real_value(v) : json_integer_value(v) static bool load_entry(struct entry *e) { struct nn_dir dir; if (!nn_dir_open(&dir, &e->path)) { return false; } str project_path; al_str_clone(&project_path, &e->path); al_str_cat(&project_path, &al_str_c("/")); al_str_cat(&project_path, &al_str_c("project.json")); json_t *root = nn_file_open_as_json(&project_path); al_str_free(&project_path); if (!root) { return false; } e->loaded = true; str package_path; al_str_clone(&package_path, &e->path); al_str_cat(&package_path, &al_str_c("/scene.pkg")); e->has_package = nn_file_exists(&package_path); if (!e->has_package) { al_str_insert(&package_path, package_path.length - 9, &al_str_c("gif")); e->has_package = nn_file_exists(&package_path); } al_str_free(&package_path); json_t *title = json_object_get(root, "title"); if (title) { al_wstr_from_cstr(&e->title, (char *)json_string_value(title)); } else { al_wstr_from_cstr(&e->title, ""); } json_t *preview = json_object_get(root, "preview"); if (preview) { al_str_clone(&e->preview_path, &e->path); al_str_cat(&e->preview_path, &al_str_c("/")); al_str_cat(&e->preview_path, &al_str_cr(json_string_value(preview))); e->has_preview = nn_file_exists(&e->preview_path); } else { e->has_preview = false; } json_t *general = json_object_get(root, "general"); if (general) { al_array_init(e->props); json_t *properties = json_object_get(general, "properties"); if (properties) { void *iter = json_object_iter(properties); json_t *iter_obj; json_t *text, *type; while (iter) { iter_obj = json_object_iter_value(iter); text = json_object_get(iter_obj, "text"); if (!text || strcmp(json_string_value(text), "ui_browse_properties_scheme_color") == 0) { iter = json_object_iter_next(properties, iter); continue; } struct prop p = { 0 }; p.type = PROP_TYPE_UNKNOWN; al_wstr_from_cstr(&p.title, (char *)json_string_value(text)); type = json_object_get(iter_obj, "type"); if (!type) { // Silently ignore for now. Is "Notes" a special case? see 1918273588. //log_warn("improperly handled prop (%.*s)", al_str_x(&e->id)); iter = json_object_iter_next(properties, iter); continue; } str type_str = al_str_cr(json_string_value(type)); if (al_str_eq(&type_str, &al_str_c("bool"))) { json_t *value = json_object_get(iter_obj, "value"); if (!value) { log_warn("improperly handled bool (%.*s)", al_str_x(&e->id)); iter = json_object_iter_next(properties, iter); continue; } p.value = al_malloc(sizeof(bool)); *((bool *)p.value) = json_boolean_value(value); p.type = PROP_TYPE_BOOL; } else if (al_str_eq(&type_str, &al_str_c("slider"))) { json_t *value = json_object_get(iter_obj, "value"); json_t *min = json_object_get(iter_obj, "min"); json_t *max = json_object_get(iter_obj, "max"); if (!value || !min || !max) { log_warn("improperly handled slider (%.*s)", al_str_x(&e->id)); iter = json_object_iter_next(properties, iter); continue; } json_t *step = json_object_get(iter_obj, "step"); p.value = al_malloc(sizeof(struct slider_value)); struct slider_value *slider = (struct slider_value *)p.value; slider->value = REAL_OR_INT(value); slider->min = REAL_OR_INT(min); slider->max = REAL_OR_INT(max); if (step) { slider->step = REAL_OR_INT(step); } else { // Assume the default step is 1.0. slider->step = 1.0; } p.type = PROP_TYPE_SLIDER; } else { //log_warn("unhandled type. (%.*s)", al_str_x(type_str)); } al_array_push(e->props, p); iter = json_object_iter_next(properties, iter); } } } json_decref(root); return e->loaded; } bool db_ensure_entry_loaded(struct db *db, struct entry *e) { if (!e->loaded && !load_entry(e)) { // Remove entry if it cannot be loaded. struct entry *re; al_array_foreach_ptr(db->entries, i, re) { if (re == e) { al_array_remove_at(db->entries, i); break; } } return false; } al_assert(e->loaded); return true; } void db_init(struct db *db) { al_array_init(db->entries); } static s32 wallpaper_id_compare(const void *a, const void *b) { s64 ac = ((struct entry *)a)->compare; s64 bc = ((struct entry *)b)->compare; if (ac > bc) return 1; else if (bc > ac) return -1; else return 0; } bool db_load_entries(struct db *db, str *path) { struct nn_dir dir; if (!nn_dir_open(&dir, path)) return false; struct nn_dir_entry entry; while (nn_dir_read(&dir, &entry)) { if (!(entry.entry->d_type == DT_DIR || entry.entry->d_type == DT_UNKNOWN) || (strcmp(entry.entry->d_name, ".") == 0) || (strcmp(entry.entry->d_name, "..") == 0)) { continue; } struct entry e = { 0 }; nn_dir_entry_get_name(&entry, &e.id); bool dupe = false; struct entry *re; al_array_foreach_ptr(db->entries, i, re) { if (al_str_eq(&re->id, &e.id)) { dupe = true; break; } } if (dupe) { al_str_free(&e.id); continue; } nn_dir_entry_get_path(&entry, &dir, &e.path); bool error; e.compare = al_str_to_long(&e.id, 10, &error); if (error) e.compare = -1; e.loaded = false; al_array_push(db->entries, e); } nn_dir_close(&dir); return true; } void db_sort(struct db *db) { al_array_sort(db->entries, struct entry, wallpaper_id_compare); } void db_unload(struct db *db) { struct entry *e; al_array_foreach_ptr(db->entries, i, e) { al_str_free(&e->id); al_str_free(&e->path); al_str_free(&e->preview_path); al_wstr_free(&e->title); struct prop *p; al_array_foreach_ptr(e->props, j, p) { al_wstr_free(&p->title); if (p->type != PROP_TYPE_UNKNOWN) { al_free(p->value); } } } }