From 230276e0378c3c8cc16a6af77a9d7f77cd8b500a Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Wed, 13 Dec 2023 00:37:39 +0000 Subject: [PATCH 1/5] Add helper to load json --- CMakeLists.txt | 2 ++ src/Abyss/CMakeLists.txt | 3 +++ src/Abyss/Common/JSON.cpp | 28 ++++++++++++++++++++++++++++ src/Abyss/Common/JSON.h | 11 +++++++++++ vcpkg.json | 4 +++- 5 files changed, 47 insertions(+), 1 deletion(-) create mode 100644 src/Abyss/Common/JSON.cpp create mode 100644 src/Abyss/Common/JSON.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a0bb66e..2dd4180 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,6 +24,8 @@ find_package(SDL2 CONFIG REQUIRED) find_package(spdlog CONFIG REQUIRED) find_package(cxxopts CONFIG REQUIRED) find_package(ZLIB REQUIRED) +find_package(nlohmann_json CONFIG REQUIRED) +find_package(RapidJSON REQUIRED) find_package(FFMPEG COMPONENTS AVCODEC AVFORMAT AVUTIL SWSCALE SWRESAMPLE REQUIRED) CPMAddPackage(NAME imgui VERSION 1.90 DOWNLOAD_ONLY YES URL https://github.com/ocornut/imgui/archive/refs/tags/v1.90.zip diff --git a/src/Abyss/CMakeLists.txt b/src/Abyss/CMakeLists.txt index d872211..ea4ecca 100644 --- a/src/Abyss/CMakeLists.txt +++ b/src/Abyss/CMakeLists.txt @@ -15,6 +15,7 @@ add_library(Abyss Common/Animation.h Common/CommandLineOpts.cpp Common/CommandLineOpts.h Common/Configuration.cpp Common/Configuration.h + Common/JSON.cpp Common/JSON.h Common/Logging.h Common/MouseProvider.h Common/MouseState.cpp Common/MouseState.h @@ -79,6 +80,8 @@ target_link_libraries(Abyss stormlib::stormlib absl::flat_hash_map absl::btree + nlohmann_json::nlohmann_json + rapidjson ${FFMPEG_LIBRARIES} ${OSX_VIDEOTOOLBOX} ${OSX_COREMEDIA} diff --git a/src/Abyss/Common/JSON.cpp b/src/Abyss/Common/JSON.cpp new file mode 100644 index 0000000..e0d7ce1 --- /dev/null +++ b/src/Abyss/Common/JSON.cpp @@ -0,0 +1,28 @@ +#include "JSON.h" +#include "Abyss/AbyssEngine.h" +#include "Abyss/FileSystem/InputStream.h" +#include +#include +#include +#include +#include +#include + +namespace Abyss::Common { + +nlohmann::json parseJson(std::string_view path) { + Abyss::FileSystem::InputStream istr = Abyss::AbyssEngine::getInstance().loadFile(path); + rapidjson::IStreamWrapper wrap(istr); + rapidjson::Document d; + d.ParseStream(wrap); + if (d.HasParseError()) { + throw std::runtime_error( + std::format("JSON parse error ({}) at offset {}", rapidjson::GetParseError_En(d.GetParseError()), d.GetErrorOffset())); + } + rapidjson::StringBuffer sb; + rapidjson::Writer writer(sb); + d.Accept(writer); + return nlohmann::json::parse(sb.GetString()); +} + +} // namespace Abyss::Common diff --git a/src/Abyss/Common/JSON.h b/src/Abyss/Common/JSON.h new file mode 100644 index 0000000..75de3a9 --- /dev/null +++ b/src/Abyss/Common/JSON.h @@ -0,0 +1,11 @@ +#pragma once + +#include +#include + +namespace Abyss::Common { + +// Like normal nlohmann, but supports comments and trailing commas +nlohmann::json parseJson(std::string_view path); + +} // namespace Abyss::Common diff --git a/vcpkg.json b/vcpkg.json index 92936d8..c18cbc4 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -4,7 +4,7 @@ "version": "0.0.1", "dependencies": [ "cxxopts", - { + { "name": "sdl2", "features": [ "alsa" @@ -15,6 +15,8 @@ "name": "sdl2", "platform": "!linux" }, + "nlohmann-json", + "rapidjson", "spdlog", "zlib", { From 3fc57452e541a13368a2bd8dd92fd11a2122475b Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Wed, 13 Dec 2023 01:58:15 +0000 Subject: [PATCH 2/5] Read profiles for layouts --- src/Abyss/CMakeLists.txt | 5 +++- src/Abyss/Common/JSON.cpp | 9 +++---- src/Abyss/Common/JSON.h | 2 +- src/Abyss/Layouts/Profile.cpp | 48 +++++++++++++++++++++++++++++++++++ src/Abyss/Layouts/Profile.h | 15 +++++++++++ 5 files changed, 71 insertions(+), 8 deletions(-) create mode 100644 src/Abyss/Layouts/Profile.cpp create mode 100644 src/Abyss/Layouts/Profile.h diff --git a/src/Abyss/CMakeLists.txt b/src/Abyss/CMakeLists.txt index ea4ecca..354b4fa 100644 --- a/src/Abyss/CMakeLists.txt +++ b/src/Abyss/CMakeLists.txt @@ -43,6 +43,8 @@ add_library(Abyss FileSystem/CASC.cpp FileSystem/CASC.h FileSystem/FileLoader.cpp FileSystem/FileLoader.h + Layouts/Profile.cpp Layouts/Profile.h + Streams/AudioStream.cpp Streams/AudioStream.h Streams/SoundEffect.cpp Streams/SoundEffect.h Streams/StreamReader.cpp Streams/StreamReader.h @@ -81,10 +83,11 @@ target_link_libraries(Abyss absl::flat_hash_map absl::btree nlohmann_json::nlohmann_json - rapidjson ${FFMPEG_LIBRARIES} ${OSX_VIDEOTOOLBOX} ${OSX_COREMEDIA} ${OSX_SECURITY} + PRIVATE + rapidjson ) diff --git a/src/Abyss/Common/JSON.cpp b/src/Abyss/Common/JSON.cpp index e0d7ce1..c29faed 100644 --- a/src/Abyss/Common/JSON.cpp +++ b/src/Abyss/Common/JSON.cpp @@ -1,6 +1,5 @@ #include "JSON.h" -#include "Abyss/AbyssEngine.h" -#include "Abyss/FileSystem/InputStream.h" +#include #include #include #include @@ -10,11 +9,9 @@ namespace Abyss::Common { -nlohmann::json parseJson(std::string_view path) { - Abyss::FileSystem::InputStream istr = Abyss::AbyssEngine::getInstance().loadFile(path); - rapidjson::IStreamWrapper wrap(istr); +nlohmann::json parseJson(std::string_view json) { rapidjson::Document d; - d.ParseStream(wrap); + d.Parse(json.data(), json.length()); if (d.HasParseError()) { throw std::runtime_error( std::format("JSON parse error ({}) at offset {}", rapidjson::GetParseError_En(d.GetParseError()), d.GetErrorOffset())); diff --git a/src/Abyss/Common/JSON.h b/src/Abyss/Common/JSON.h index 75de3a9..e039477 100644 --- a/src/Abyss/Common/JSON.h +++ b/src/Abyss/Common/JSON.h @@ -6,6 +6,6 @@ namespace Abyss::Common { // Like normal nlohmann, but supports comments and trailing commas -nlohmann::json parseJson(std::string_view path); +nlohmann::json parseJson(std::string_view json); } // namespace Abyss::Common diff --git a/src/Abyss/Layouts/Profile.cpp b/src/Abyss/Layouts/Profile.cpp new file mode 100644 index 0000000..41895e8 --- /dev/null +++ b/src/Abyss/Layouts/Profile.cpp @@ -0,0 +1,48 @@ +#include "Profile.h" +#include "Abyss/Common/JSON.h" +#include "Abyss/Singletons.h" +#include +#include +#include + +namespace Abyss::Layouts { + +namespace { + +nlohmann::json readMergedProfile(std::string_view name) { + nlohmann::json me = + Abyss::Common::parseJson(Singletons::getFileProvider().loadString(std::format("/data/global/ui/layouts/_profile{}.json", name))); + if (me.contains("basedOn")) { + nlohmann::json base = readMergedProfile(me["basedOn"].get()); + base.merge_patch(me); + return base; + } + return me; +} + +bool resolveDataReferences(nlohmann::json &object, const nlohmann::json &profile) { + bool again = false; + for (auto &[k, v] : object.items()) { + if (v.is_object()) { + again = resolveDataReferences(v, profile) || again; + } else if (v.is_string() && v.get().starts_with('$')) { + v = profile.at(v.get().substr(1)); + again = true; + } + } + return again; +} + +} // namespace + +Profile::Profile(std::string_view name) { + _data = readMergedProfile(name); + while (resolveDataReferences(_data, _data)) { + } +} + +void Profile::resolveReferences(nlohmann::json& object) { + resolveDataReferences(object, _data); +} + +} // namespace Abyss::Layouts diff --git a/src/Abyss/Layouts/Profile.h b/src/Abyss/Layouts/Profile.h new file mode 100644 index 0000000..2c42a81 --- /dev/null +++ b/src/Abyss/Layouts/Profile.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Abyss::Layouts { + +class Profile { + nlohmann::json _data; + public: + explicit Profile(std::string_view name); + void resolveReferences(nlohmann::json& object); +}; + +} // namespace Abyss::Layouts From 9b8d3e5311f8f5ddd6ae5d0b80c993ce008f75e0 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Thu, 14 Dec 2023 02:16:17 +0000 Subject: [PATCH 3/5] Read layout from json --- src/Abyss/CMakeLists.txt | 1 + src/Abyss/Layouts/Layout.cpp | 53 ++++++++++++++++++++++++++++++++++++ src/Abyss/Layouts/Layout.h | 15 ++++++++++ src/Abyss/Layouts/Profile.h | 1 + 4 files changed, 70 insertions(+) create mode 100644 src/Abyss/Layouts/Layout.cpp create mode 100644 src/Abyss/Layouts/Layout.h diff --git a/src/Abyss/CMakeLists.txt b/src/Abyss/CMakeLists.txt index 354b4fa..f871cd9 100644 --- a/src/Abyss/CMakeLists.txt +++ b/src/Abyss/CMakeLists.txt @@ -43,6 +43,7 @@ add_library(Abyss FileSystem/CASC.cpp FileSystem/CASC.h FileSystem/FileLoader.cpp FileSystem/FileLoader.h + Layouts/Layout.cpp Layouts/Layout.h Layouts/Profile.cpp Layouts/Profile.h Streams/AudioStream.cpp Streams/AudioStream.h diff --git a/src/Abyss/Layouts/Layout.cpp b/src/Abyss/Layouts/Layout.cpp new file mode 100644 index 0000000..5c8b7b5 --- /dev/null +++ b/src/Abyss/Layouts/Layout.cpp @@ -0,0 +1,53 @@ +#include "Layout.h" +#include "Abyss/Common/JSON.h" +#include "Abyss/Singletons.h" +#include +#include + +namespace Abyss::Layouts { + +namespace { + +void mergeFromParent(nlohmann::json &object, nlohmann::json &parent) { + if (parent.contains("fields")) { + nlohmann::json fields = std::move(parent["fields"]); + if (object.contains("fields")) { + fields.merge_patch(object["fields"]); + } + object["fields"] = std::move(fields); + } + if (object.contains("children") && parent.contains("children")) { + absl::flat_hash_map brothers; + for (auto &c : parent["children"]) { + if (c.contains("name")) { + brothers[c["name"].get()] = &c; + } + } + for (auto &c : object["children"]) { + if (c.contains("name")) { + auto it = brothers.find(c["name"].get()); + if (it != brothers.end()) { + nlohmann::json &brother = *it->second; + mergeFromParent(c, brother); + } + } + } + } +} + +nlohmann::json readMergedLayout(std::string_view name) { + nlohmann::json me = Abyss::Common::parseJson(Singletons::getFileProvider().loadString(absl::StrCat("/data/global/ui/layouts/", name))); + if (me.contains("basedOn")) { + nlohmann::json parent = readMergedLayout(me["basedOn"].get()); + mergeFromParent(me, parent); + } + return me; +} + +} // namespace + +Layout::Layout(std::string_view name) { + _data = readMergedLayout(name); +} + +} // namespace Abyss::Layouts diff --git a/src/Abyss/Layouts/Layout.h b/src/Abyss/Layouts/Layout.h new file mode 100644 index 0000000..2cbf9c4 --- /dev/null +++ b/src/Abyss/Layouts/Layout.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +namespace Abyss::Layouts { + +class Layout { + nlohmann::json _data; + public: + // Reads from /data/global/ui/layouts/{name} (name should include .json extension) + explicit Layout(std::string_view name); +}; + +} // namespace Abyss::Layouts diff --git a/src/Abyss/Layouts/Profile.h b/src/Abyss/Layouts/Profile.h index 2c42a81..b88188a 100644 --- a/src/Abyss/Layouts/Profile.h +++ b/src/Abyss/Layouts/Profile.h @@ -8,6 +8,7 @@ namespace Abyss::Layouts { class Profile { nlohmann::json _data; public: + // Reads from /data/global/ui/layouts/_profile{name}.json explicit Profile(std::string_view name); void resolveReferences(nlohmann::json& object); }; From ec617aab1cc4416c25f9f4abe399a37e0371c2f8 Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Thu, 14 Dec 2023 02:31:58 +0000 Subject: [PATCH 4/5] Apply profile to layout --- src/Abyss/Layouts/Layout.cpp | 3 ++- src/Abyss/Layouts/Layout.h | 4 +++- src/Abyss/Layouts/Profile.cpp | 4 ++-- src/Abyss/Layouts/Profile.h | 2 +- 4 files changed, 8 insertions(+), 5 deletions(-) diff --git a/src/Abyss/Layouts/Layout.cpp b/src/Abyss/Layouts/Layout.cpp index 5c8b7b5..ed830f6 100644 --- a/src/Abyss/Layouts/Layout.cpp +++ b/src/Abyss/Layouts/Layout.cpp @@ -46,8 +46,9 @@ nlohmann::json readMergedLayout(std::string_view name) { } // namespace -Layout::Layout(std::string_view name) { +Layout::Layout(std::string_view name, const Profile& profile) { _data = readMergedLayout(name); + profile.resolveReferences(_data); } } // namespace Abyss::Layouts diff --git a/src/Abyss/Layouts/Layout.h b/src/Abyss/Layouts/Layout.h index 2cbf9c4..1573e72 100644 --- a/src/Abyss/Layouts/Layout.h +++ b/src/Abyss/Layouts/Layout.h @@ -1,5 +1,6 @@ #pragma once +#include "Profile.h" #include #include @@ -7,9 +8,10 @@ namespace Abyss::Layouts { class Layout { nlohmann::json _data; + public: // Reads from /data/global/ui/layouts/{name} (name should include .json extension) - explicit Layout(std::string_view name); + explicit Layout(std::string_view name, const Profile &profile); }; } // namespace Abyss::Layouts diff --git a/src/Abyss/Layouts/Profile.cpp b/src/Abyss/Layouts/Profile.cpp index 41895e8..c8d80e2 100644 --- a/src/Abyss/Layouts/Profile.cpp +++ b/src/Abyss/Layouts/Profile.cpp @@ -23,7 +23,7 @@ nlohmann::json readMergedProfile(std::string_view name) { bool resolveDataReferences(nlohmann::json &object, const nlohmann::json &profile) { bool again = false; for (auto &[k, v] : object.items()) { - if (v.is_object()) { + if (v.is_structured()) { again = resolveDataReferences(v, profile) || again; } else if (v.is_string() && v.get().starts_with('$')) { v = profile.at(v.get().substr(1)); @@ -41,7 +41,7 @@ Profile::Profile(std::string_view name) { } } -void Profile::resolveReferences(nlohmann::json& object) { +void Profile::resolveReferences(nlohmann::json& object) const { resolveDataReferences(object, _data); } diff --git a/src/Abyss/Layouts/Profile.h b/src/Abyss/Layouts/Profile.h index b88188a..7b9e21e 100644 --- a/src/Abyss/Layouts/Profile.h +++ b/src/Abyss/Layouts/Profile.h @@ -10,7 +10,7 @@ class Profile { public: // Reads from /data/global/ui/layouts/_profile{name}.json explicit Profile(std::string_view name); - void resolveReferences(nlohmann::json& object); + void resolveReferences(nlohmann::json& object) const; }; } // namespace Abyss::Layouts From 66820324a6ded4a38fc7a2aee79c4be20c5e3b8f Mon Sep 17 00:00:00 2001 From: Alexey Sokolov Date: Sat, 16 Dec 2023 23:50:38 +0000 Subject: [PATCH 5/5] Move Layouts/ to OD2 --- src/Abyss/CMakeLists.txt | 3 --- src/OD2/CMakeLists.txt | 3 +++ src/{Abyss => OD2}/Layouts/Layout.cpp | 8 ++++---- src/{Abyss => OD2}/Layouts/Layout.h | 4 ++-- src/{Abyss => OD2}/Layouts/Profile.cpp | 10 ++++------ src/{Abyss => OD2}/Layouts/Profile.h | 7 ++++--- 6 files changed, 17 insertions(+), 18 deletions(-) rename src/{Abyss => OD2}/Layouts/Layout.cpp (85%) rename src/{Abyss => OD2}/Layouts/Layout.h (84%) rename src/{Abyss => OD2}/Layouts/Profile.cpp (77%) rename src/{Abyss => OD2}/Layouts/Profile.h (66%) diff --git a/src/Abyss/CMakeLists.txt b/src/Abyss/CMakeLists.txt index f871cd9..ad2a192 100644 --- a/src/Abyss/CMakeLists.txt +++ b/src/Abyss/CMakeLists.txt @@ -43,9 +43,6 @@ add_library(Abyss FileSystem/CASC.cpp FileSystem/CASC.h FileSystem/FileLoader.cpp FileSystem/FileLoader.h - Layouts/Layout.cpp Layouts/Layout.h - Layouts/Profile.cpp Layouts/Profile.h - Streams/AudioStream.cpp Streams/AudioStream.h Streams/SoundEffect.cpp Streams/SoundEffect.h Streams/StreamReader.cpp Streams/StreamReader.h diff --git a/src/OD2/CMakeLists.txt b/src/OD2/CMakeLists.txt index b76409b..3fe7b0d 100644 --- a/src/OD2/CMakeLists.txt +++ b/src/OD2/CMakeLists.txt @@ -22,6 +22,9 @@ target_sources(OpenDiablo2 # Scenes # - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Layouts/Layout.cpp Layouts/Layout.h + Layouts/Profile.cpp Layouts/Profile.h + # Main Menu Scenes/MainMenu/MainMenu.cpp Scenes/MainMenu/MainMenu.h Scenes/MainMenu/Logo.cpp Scenes/MainMenu/Logo.h diff --git a/src/Abyss/Layouts/Layout.cpp b/src/OD2/Layouts/Layout.cpp similarity index 85% rename from src/Abyss/Layouts/Layout.cpp rename to src/OD2/Layouts/Layout.cpp index ed830f6..494808e 100644 --- a/src/Abyss/Layouts/Layout.cpp +++ b/src/OD2/Layouts/Layout.cpp @@ -4,7 +4,7 @@ #include #include -namespace Abyss::Layouts { +namespace OD2::Layouts { namespace { @@ -36,7 +36,7 @@ void mergeFromParent(nlohmann::json &object, nlohmann::json &parent) { } nlohmann::json readMergedLayout(std::string_view name) { - nlohmann::json me = Abyss::Common::parseJson(Singletons::getFileProvider().loadString(absl::StrCat("/data/global/ui/layouts/", name))); + nlohmann::json me = Abyss::Common::parseJson(Abyss::Singletons::getFileProvider().loadString(absl::StrCat("/data/global/ui/layouts/", name))); if (me.contains("basedOn")) { nlohmann::json parent = readMergedLayout(me["basedOn"].get()); mergeFromParent(me, parent); @@ -46,9 +46,9 @@ nlohmann::json readMergedLayout(std::string_view name) { } // namespace -Layout::Layout(std::string_view name, const Profile& profile) { +Layout::Layout(std::string_view name, const Profile &profile) { _data = readMergedLayout(name); profile.resolveReferences(_data); } -} // namespace Abyss::Layouts +} // namespace OD2::Layouts diff --git a/src/Abyss/Layouts/Layout.h b/src/OD2/Layouts/Layout.h similarity index 84% rename from src/Abyss/Layouts/Layout.h rename to src/OD2/Layouts/Layout.h index 1573e72..1233fb8 100644 --- a/src/Abyss/Layouts/Layout.h +++ b/src/OD2/Layouts/Layout.h @@ -4,7 +4,7 @@ #include #include -namespace Abyss::Layouts { +namespace OD2::Layouts { class Layout { nlohmann::json _data; @@ -14,4 +14,4 @@ class Layout { explicit Layout(std::string_view name, const Profile &profile); }; -} // namespace Abyss::Layouts +} // namespace OD2::Layouts diff --git a/src/Abyss/Layouts/Profile.cpp b/src/OD2/Layouts/Profile.cpp similarity index 77% rename from src/Abyss/Layouts/Profile.cpp rename to src/OD2/Layouts/Profile.cpp index c8d80e2..5e49c89 100644 --- a/src/Abyss/Layouts/Profile.cpp +++ b/src/OD2/Layouts/Profile.cpp @@ -5,13 +5,13 @@ #include #include -namespace Abyss::Layouts { +namespace OD2::Layouts { namespace { nlohmann::json readMergedProfile(std::string_view name) { nlohmann::json me = - Abyss::Common::parseJson(Singletons::getFileProvider().loadString(std::format("/data/global/ui/layouts/_profile{}.json", name))); + Abyss::Common::parseJson(Abyss::Singletons::getFileProvider().loadString(std::format("/data/global/ui/layouts/_profile{}.json", name))); if (me.contains("basedOn")) { nlohmann::json base = readMergedProfile(me["basedOn"].get()); base.merge_patch(me); @@ -41,8 +41,6 @@ Profile::Profile(std::string_view name) { } } -void Profile::resolveReferences(nlohmann::json& object) const { - resolveDataReferences(object, _data); -} +void Profile::resolveReferences(nlohmann::json &object) const { resolveDataReferences(object, _data); } -} // namespace Abyss::Layouts +} // namespace OD2::Layouts diff --git a/src/Abyss/Layouts/Profile.h b/src/OD2/Layouts/Profile.h similarity index 66% rename from src/Abyss/Layouts/Profile.h rename to src/OD2/Layouts/Profile.h index 7b9e21e..47271ee 100644 --- a/src/Abyss/Layouts/Profile.h +++ b/src/OD2/Layouts/Profile.h @@ -3,14 +3,15 @@ #include #include -namespace Abyss::Layouts { +namespace OD2::Layouts { class Profile { nlohmann::json _data; + public: // Reads from /data/global/ui/layouts/_profile{name}.json explicit Profile(std::string_view name); - void resolveReferences(nlohmann::json& object) const; + void resolveReferences(nlohmann::json &object) const; }; -} // namespace Abyss::Layouts +} // namespace OD2::Layouts