#pragma once #include "common.h" #include #include #include using simdjson::dom::array; using simdjson::dom::element; using simdjson::dom::object; // A document holding an empty object and array, standing in for whatever's missing so lookups never fail. inline element json_empty_get(const char* key) { static simdjson::padded_string text(std::string_view(R"({"o":{},"a":[]})")); static simdjson::dom::parser parser; static element document = parser.parse(text).value_unsafe(); return document[key].value_unsafe(); } // A parsed JSON file. Its objects stay readable while it lives. struct Json { simdjson::dom::parser parser; object root = json_empty_get("o").get_object().value_unsafe(); bool isValid = false; void load(const std::string& path) { isValid = !parser.load(path).get(root); if (!isValid) SDL_Log("Failed to load %s", path.c_str()); } }; inline float json_float(object source, const char* key, float fallback = 0) { double value; return source[key].get(value) ? fallback : (float)value; } inline int json_int(object source, const char* key, int fallback = 0) { return (int)json_float(source, key, (float)fallback); } inline bool json_is(object source, const char* key, bool fallback = false) { bool value; return source[key].get(value) ? fallback : value; } inline std::string json_string(object source, const char* key, const std::string& fallback = "") { std::string_view value; return source[key].get(value) ? fallback : std::string(value); } inline object json_object(object source, const char* key) { object value; return source[key].get(value) ? json_empty_get("o").get_object().value_unsafe() : value; } inline array json_array(object source, const char* key) { array value; return source[key].get(value) ? json_empty_get("a").get_array().value_unsafe() : value; } // A list of numbers read into values, keeping each fallback where the list stops short. inline void json_floats(object source, const char* key, float* values, int count) { array list = json_array(source, key); for (int i = 0; i < count; i++) { double value; if (!list.at(i).get(value)) values[i] = (float)value; } } inline Vec2 json_vec2(object source, const char* key, Vec2 fallback = {}) { json_floats(source, key, &fallback.x, 2); return fallback; } // A color as a list of three or four numbers, scaled down by divisor. inline Color json_color(object source, const char* key, Color fallback = WHITE, float divisor = 1) { Color value = {fallback.r * divisor, fallback.g * divisor, fallback.b * divisor, fallback.a * divisor}; json_floats(source, key, &value.r, 4); return {value.r / divisor, value.g / divisor, value.b / divisor, value.a / divisor}; } // A list of strings, or one string as a list of it. inline std::vector json_strings(object source, const char* key) { std::vector values; std::string_view single; if (!source[key].get(single)) return {std::string(single)}; for (element entry : json_array(source, key)) { std::string_view value; if (!entry.get(value)) values.emplace_back(value); } return values; } // Logs any key of an object its reader doesn't know (likely a typo), naming where it is. inline void json_keys_check(object source, std::initializer_list keys, const std::string& where) { for (auto [key, value] : source) if (std::find_if(keys.begin(), keys.end(), [&](const char* known) { return key == known; }) == keys.end()) SDL_Log("Unknown key \"%s\" in %s", std::string(key).c_str(), where.c_str()); }