yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
terrain_catalog.cc
Go to the documentation of this file.
2
3#include <cstdint>
4#include <iomanip>
5#include <set>
6#include <sstream>
7
8namespace yaze::core {
9namespace {
10bool Text(const nlohmann::json& j, const char* key) {
11 return j.contains(key) && j[key].is_string() &&
12 !j[key].get_ref<const std::string&>().empty();
13}
14absl::Status Invalid(const char* message) {
15 return absl::InvalidArgumentError(message);
16}
17} // namespace
18
19absl::StatusOr<TerrainCatalog> TerrainCatalog::Parse(std::string_view text) {
20 TerrainCatalog result;
21 try {
22 auto j = nlohmann::json::parse(text);
23 if (!j.is_object() || !j.contains("schema_version") ||
24 !j["schema_version"].is_number_integer() || j["schema_version"] != 1 ||
25 !Text(j, "game_id") || !j.contains("entries") ||
26 !j["entries"].is_array()) {
27 return Invalid(
28 "Terrain catalog requires schema_version 1, game_id and entries "
29 "array");
30 }
31 std::set<std::string> ids;
32 for (const auto& entry : j["entries"]) {
33 if (!entry.is_object() || !Text(entry, "id") || !Text(entry, "name") ||
34 !Text(entry, "category") || !Text(entry, "review_state") ||
35 !entry.contains("bindings") || !entry["bindings"].is_array()) {
36 return Invalid(
37 "Terrain entry requires id, name, category, review_state and "
38 "bindings");
39 }
40 if (!ids.insert(entry["id"].get<std::string>()).second) {
41 return Invalid("Duplicate terrain entry id");
42 }
43 const auto state = entry["review_state"].get<std::string>();
44 if (state != "suggested" && state != "reviewed") {
45 return Invalid("Terrain review_state must be suggested or reviewed");
46 }
47 if (entry.contains("tags")) {
48 if (!entry["tags"].is_array())
49 return Invalid("Terrain tags must be an array");
50 for (const auto& tag : entry["tags"]) {
51 if (!tag.is_string())
52 return Invalid("Terrain tags must be strings");
53 }
54 }
55 for (const auto& binding : entry["bindings"]) {
56 if (!binding.is_object() || !Text(binding, "adapter") ||
57 !Text(binding, "definition_fingerprint") ||
58 !Text(binding, "profile_fingerprint") ||
59 !binding.contains("tile_id") ||
60 !binding["tile_id"].is_number_integer() || binding["tile_id"] < 0 ||
61 binding["tile_id"] > 0x7fffffff) {
62 return Invalid(
63 "Terrain binding requires adapter, nonnegative tile_id and exact "
64 "fingerprints");
65 }
66 }
67 }
68 result.document_ = std::move(j);
69 } catch (const nlohmann::json::exception& e) {
70 return absl::InvalidArgumentError(std::string("Invalid terrain catalog: ") +
71 e.what());
72 }
73 return result;
74}
75
76std::vector<nlohmann::json> TerrainCatalog::Resolve(
77 std::string_view adapter, int tile_id, std::string_view definition,
78 std::string_view profile) const {
79 std::vector<nlohmann::json> result;
80 for (const auto& entry : document_["entries"]) {
81 for (const auto& binding : entry["bindings"]) {
82 if (std::string_view(binding["adapter"].get_ref<const std::string&>()) ==
83 adapter &&
84 binding["tile_id"] == tile_id &&
85 std::string_view(binding["definition_fingerprint"]
86 .get_ref<const std::string&>()) == definition &&
87 std::string_view(
88 binding["profile_fingerprint"].get_ref<const std::string&>()) ==
89 profile) {
90 result.push_back(entry);
91 break;
92 }
93 }
94 }
95 return result;
96}
97
98std::string TerrainFingerprint(std::string_view bytes) {
99 uint64_t hash = 14695981039346656037ULL;
100 for (unsigned char byte : bytes) {
101 hash ^= byte;
102 hash *= 1099511628211ULL;
103 }
104 std::ostringstream stream;
105 stream << "fnv1a64:" << std::hex << std::setfill('0') << std::setw(16)
106 << hash;
107 return stream.str();
108}
109} // namespace yaze::core
std::vector< nlohmann::json > Resolve(std::string_view adapter, int tile_id, std::string_view definition, std::string_view profile) const
static absl::StatusOr< TerrainCatalog > Parse(std::string_view text)
std::string TerrainFingerprint(std::string_view bytes)