120 lines
3.7 KiB
C++
120 lines
3.7 KiB
C++
#pragma once
|
|
|
|
#include "common.h"
|
|
#include <simdjson.h>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
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<std::string> json_strings(object source, const char* key)
|
|
{
|
|
std::vector<std::string> 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<const char*> 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());
|
|
}
|