#include "Json.hpp" #include #include "../io/File.hpp" #include "../io/Logger.hpp" #include "../math/Geometry.hpp" #include "../string/String.hpp" namespace ostd { namespace detail { template struct Getter; // ----- bool --------------------------------------------------------- template<> struct Getter { static bool exec_impl(const std::string& p, const json& root) { try { return root.at(p).get(); } catch (const json::exception&) { return false; } } }; // ----- i32 ---------------------------------------------------------- template<> struct Getter { static i32 exec_impl(const std::string& p, const json& root) { try { return root.at(p).get(); } catch (const json::exception&) { return 0; } } }; // ----- f64 ------------------------------------------------------- template<> struct Getter { static f64 exec_impl(const std::string& p, const json& root) { try { return root.at(p).get(); } catch (const json::exception&) { return 0.0; } } }; // ----- String ------------------------------------------------- template<> struct Getter { static String exec_impl(const std::string& p, const json& root) { try { return String(root.at(p).get().c_str()); } catch (const json::exception&) { return String(""); } } }; // ----- Color ------------------------------------------------- template<> struct Getter { static Color exec_impl(const std::string& p, const json& root) { try { const json& node = root.at(p); // Case 1: Hex string ("#RRGGBBAA") if (node.is_string()) { const std::string& hex = node.get(); if (hex.starts_with("#") && hex.size() == 9) // 9 for #RRGGBBAA return Color(hex); return Color(); // Not a valid hex format } // Case 2: Array of integers if (node.is_array() && !node.empty()) { stdvec vals; for (const auto& v : node) { if (!v.is_number_integer()) return Color(); i32 iv = v.get(); if (iv < 0 || iv > 255) return Color(); vals.push_back(iv); } switch (vals.size()) { case 1: return Color(vals[0], vals[0], vals[0]); case 3: return Color(vals[0], vals[1], vals[2]); case 4: return Color(vals[0], vals[1], vals[2], vals[3]); default: return Color(); } } // Anything else → default color return Color(); } catch (...) { return Color(); } } }; // ----- Rectangle ------------------------------------------------- template<> struct Getter { static Rectangle exec_impl(const std::string& p, const json& root) { try { const json& node = root.at(p); if (node.is_array() && node.size() == 4) { stdvec vals; vals.reserve(4); for (const auto& v : node) { if (!v.is_number_float() && !v.is_number_integer()) return Rectangle(); vals.push_back(v.get()); } return Rectangle(vals[0], vals[1], vals[2], vals[3]); } return Rectangle(); } catch (...) { return Rectangle(); } } }; // ----- stdvec ---------------------------------------- template<> struct Getter> { static stdvec exec_impl(const std::string& p, const json& root) { stdvec result; try { const json& arr = root.at(p); if (!arr.is_array()) return result; for (const auto& v : arr) { if (v.is_number_integer()) result.push_back(v.get()); } } catch (...) {} return result; } }; // ----- stdvec ----------------------------------------- template<> struct Getter> { static stdvec exec_impl(const std::string& p, const json& root) { stdvec result; try { const json& arr = root.at(p); if (!arr.is_array()) return result; for (const auto& v : arr) { if (v.is_number()) result.push_back(v.get()); } } catch (...) {} return result; } }; // ----- stdvec ----------------------------------- template<> struct Getter> { static stdvec exec_impl(const std::string& p, const json& root) { stdvec result; try { const json& arr = root.at(p); if (!arr.is_array()) return result; for (const auto& v : arr) { if (v.is_string()) result.emplace_back(v.get().c_str()); } } catch (...) {} return result; } }; // ----- stdvec ----------------------------------- template<> struct Getter> { static stdvec exec_impl(const std::string& p, const json& root) { stdvec result; try { const json& node = root.at(p); if (!node.is_array() || node.empty()) return result; result.reserve(node.size()); for (const auto& element : node) { // We temporarily create a fake "sub-root" that only contains one key "color" // This lets us call the existing Getter without modifying it json fake_root = { { "color", element } }; Color color = Getter::exec_impl("color", fake_root); result.push_back(color); } return result; } catch (...) { return {}; } } }; // ----- stdvec ----------------------------------- template<> struct Getter { static Vec2 exec_impl(const std::string& p, const json& root) { try { const json& arr = root.at(p); if (!arr.is_array() || arr.size() != 2) return Vec2(); return Vec2(arr[0].get(), arr[1].get()); } catch (...) { return Vec2(); } } }; // ----- stdvec ----------------------------------- template<> struct Getter> { static stdvec exec_impl(const std::string& p, const json& root) { stdvec result; try { const json& node = root.at(p); if (!node.is_array() || node.empty()) return result; result.reserve(node.size()); for (const auto& element : node) { json fake_root = { { "rect", element } }; Rectangle rect = Getter::exec_impl("rect", fake_root); result.push_back(rect); } return result; } catch (...) { return {}; } } }; template struct Setter; // ----- bool --------------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, bool value) { try { root[p] = value; return true; } catch (...) { return false; } } }; // ----- i32 ---------------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, i32 value) { try { root[p] = value; return true; } catch (...) { return false; } } }; // ----- f64 ------------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, f64 value) { try { root[p] = value; return true; } catch (...) { return false; } } }; // ----- String ------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, const String& value) { try { root[p] = value; return true; } catch (...) { return false; } } }; // ----- Color ------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, const Color& value) { try { json arr = json::array(); arr.push_back(cast(value.r)); arr.push_back(cast(value.g)); arr.push_back(cast(value.b)); if (value.a != 255) arr.push_back(cast(value.a)); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ----------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, const Vec2& value) { try { json arr = json::array(); arr.push_back(value.x); arr.push_back(value.y); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- Rectangle ------------------------------------------------- template<> struct Setter { static bool exec_impl(const std::string& p, json& root, const Rectangle& value) { try { json arr = json::array(); arr.push_back(value.x); arr.push_back(value.y); arr.push_back(value.w); arr.push_back(value.h); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ---------------------------------------- template<> struct Setter> { static bool exec_impl(const std::string& p, json& root, const stdvec& value) { try { json arr = json::array(); for (i32 v : value) arr.push_back(v); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ----------------------------------------- template<> struct Setter> { static bool exec_impl(const std::string& p, json& root, const stdvec& value) { try { json arr = json::array(); for (f64 v : value) arr.push_back(v); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ----------------------------------- template<> struct Setter> { static bool exec_impl(const std::string& p, json& root, const stdvec& value) { try { json arr = json::array(); for (const auto& s : value) arr.push_back(s.cpp_str()); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ----------------------------------- template<> struct Setter> { static bool exec_impl(const std::string& p, json& root, const stdvec& value) { try { json arr = json::array(); for (const auto& color : value) arr.push_back(color.hexString(true, "#").cpp_str()); root[p] = arr; return true; } catch (...) { return false; } } }; // ----- stdvec ----------------------------------- template<> struct Setter> { static bool exec_impl(const std::string& p, json& root, const stdvec& value) { try { json arr = json::array(); for (const auto& rect : value) { json rect_arr = json::array(); rect_arr.push_back(rect.x); rect_arr.push_back(rect.y); rect_arr.push_back(rect.w); rect_arr.push_back(rect.h); arr.push_back(rect_arr); } root[p] = arr; return true; } catch (...) { return false; } } }; } static inline bool __navigate_json_path(const String& path, json*& outSource, String& outName) { auto tokens = path.tokenize("."); if (tokens.count() <= 1) { if (!outSource->contains(path)) return false; // path broken outName = path; return true; } String key = ""; while (tokens.hasNext()) { key = tokens.next(); if (!outSource->contains(key)) return false; // path broken if (!tokens.hasNext()) { outName = key; return true; } const json& child = (*outSource)[key]; if (!child.is_object()) return false; // path broken outSource = const_cast(&child); } return false; } template T JsonFile::get(const String& name) { json* source = &m_json; String settingName = ""; if (__navigate_json_path(name, source, settingName)) return detail::Getter::exec_impl(settingName, *source); else OX_ERROR("Setting not found: %s", name.c_str()); return detail::Getter::exec_impl(settingName, *source); } template bool JsonFile::set(const String& name, T value) { if (!m_loaded) return false; if (!m_writeable) return false; auto tokens = name.tokenize("."); if (tokens.count() == 0) return false; json* current = &m_json; String key; while (tokens.hasNext()) { key = tokens.next(); if (!tokens.hasNext()) break; if (!current->contains(key.cpp_str()) || !(*current)[key.cpp_str()].is_object()) (*current)[key.cpp_str()] = json::object(); current = &(*current)[key.cpp_str()]; } // Set the leaf value if (detail::Setter::exec_impl(key.cpp_str(), *current, value)) return __write_to_file(); OX_ERROR("Failed to set value for: %s", name.c_str()); return false; } bool JsonFile::get_bool(const String& name) { return get(name); } i32 JsonFile::get_int(const String& name) { return get(name); } f64 JsonFile::get_double(const String& name) { return get(name); } String JsonFile::get_string(const String& name) { return get(name); } Color JsonFile::get_color(const String& name) { return get(name); } Vec2 JsonFile::get_vec2(const String& name) { return get(name); } Rectangle JsonFile::get_rect(const String& name) { return get(name); } stdvec JsonFile::get_int_array(const String& name) { return get>(name); } stdvec JsonFile::get_double_array(const String& name) { return get>(name); } stdvec JsonFile::get_string_array(const String& name) { return get>(name); } stdvec JsonFile::get_color_array(const String& name) { return get>(name); } stdvec JsonFile::get_rect_array(const String& name) { return get>(name); } bool JsonFile::set_bool(const String& name, bool value) { return set(name, value); } bool JsonFile::set_int(const String& name, i32 value) { return set(name, value); } bool JsonFile::set_double(const String& name, f64 value) { return set(name, value); } bool JsonFile::set_string(const String& name, const String& value) { return set(name, value); } bool JsonFile::set_color(const String& name, const Color& value) { return set(name, value); } bool JsonFile::set_vec2(const String& name, const Vec2& value) { return set(name, value); } bool JsonFile::set_rect(const String& name, const Rectangle& value) { return set(name, value); } bool JsonFile::set_int_array(const String& name, const stdvec& value) { return set>(name, value); } bool JsonFile::set_double_array(const String& name, const stdvec& value) { return set>(name, value); } bool JsonFile::set_string_array(const String& name, const stdvec& value) { return set>(name, value); } bool JsonFile::set_color_array(const String& name, const stdvec& value) { return set>(name, value); } bool JsonFile::set_rect_array(const String& name, const stdvec& value) { return set>(name, value); } bool JsonFile::init(const String& filePath, bool writeable, const json* obj) { m_filePath = filePath; m_rawJson = ""; m_loaded = false; m_writeable = writeable; if (obj != nullptr) m_json = *obj; ostd::TextFileBuffer jsonFile(filePath); if (!jsonFile.exists()) { if (!writeable) { OX_ERROR("JSON read-only file doesn't exist: %s", filePath.c_str()); return false; } m_loaded = true; __write_to_file(); m_loaded = false; } jsonFile = ostd::TextFileBuffer(filePath); try { m_json = json::parse(jsonFile.rawContent()); } catch (const json::parse_error& e) { OX_ERROR("JSON parse failed: %s", e.what()); return false; } m_rawJson = jsonFile.rawContent(); m_loaded = true; OX_DEBUG("Loaded JSON file: <%s>", filePath.c_str()); return true; } bool JsonFile::__write_to_file(const json* obj) { if (!m_loaded) return false; if (obj == nullptr) obj = &m_json; try { std::ofstream file(m_filePath.cpp_str(), std::ios::trunc); if (!file) throw std::runtime_error("Failed to open file."); file << obj->dump(4); if (file.fail()) throw std::runtime_error("Failed to write to file"); file.flush(); if (file.fail()) throw std::runtime_error("Failed to flush"); } catch (const std::exception& e) { OX_ERROR("Failed to write JSON file: %s", e.what()); return false; } return true; } }