yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
sprite_asset_json.h
Go to the documentation of this file.
1#ifndef YAZE_CORE_SPRITE_ASSET_JSON_H_
2#define YAZE_CORE_SPRITE_ASSET_JSON_H_
3#include <algorithm>
4#include <limits>
5#include <set>
6#include <stdexcept>
7#include <vector>
8
9#include "absl/status/statusor.h"
10#include "core/sprite_asset.h"
12#include "nlohmann/json.hpp"
13namespace yaze::project {
14inline nlohmann::json SpriteAssetToJson(const SpriteAssetBinding& asset) {
15 nlohmann::json rows = nlohmann::json::array();
16 for (const auto& row : asset.palette_rows)
17 rows.push_back({{"group", row.group}, {"index", row.index}});
18 nlohmann::json result = {{"version", 1},
19 {"zsm_path", asset.zsm_path},
20 {"catalog_key", asset.catalog_key},
21 {"draw_adapter", asset.draw_adapter},
22 {"source_path", asset.source_path},
23 {"source_label", asset.source_label},
24 {"source_sha256", asset.source_sha256},
25 {"sheets", asset.sheets},
26 {"palette_rows", rows}};
27 if (!asset.behavior.profile.empty()) {
28 result["version"] = 2;
29 result["behavior"] = SpriteBehaviorToJson(asset.behavior);
30 }
31 if (std::any_of(asset.graphics_sources.begin(), asset.graphics_sources.end(),
32 [](const auto& source) {
33 return source.kind != "sheet" || !source.path.empty() ||
34 source.offset != 0 || source.bpp != 3;
35 })) {
36 result["version"] = 3;
37 result["graphics_sources"] = nlohmann::json::array();
38 for (const auto& source : asset.graphics_sources)
39 result["graphics_sources"].push_back({{"kind", source.kind},
40 {"path", source.path},
41 {"offset", source.offset},
42 {"bpp", source.bpp}});
43 }
44 return result;
45}
46
47inline absl::StatusOr<SpriteAssetBinding> SpriteAssetFromJson(
48 const nlohmann::json& value) {
49 auto invalid = [] {
50 return absl::InvalidArgumentError("Invalid sprite asset binding v1");
51 };
52 try {
53 if (!value.is_object() || !value.at("version").is_number_integer())
54 return invalid();
55 const bool graphics_version = value.at("version") == 3;
56 const bool has_behavior = value.contains("behavior");
57 if ((value.at("version") != 1 && value.at("version") != 2 &&
58 !graphics_version) ||
59 (!graphics_version && has_behavior != (value.at("version") == 2)) ||
60 value.size() != 9 + (has_behavior ? 1 : 0) + (graphics_version ? 1 : 0))
61 return invalid();
63 if (has_behavior) {
64 auto behavior = SpriteBehaviorFromJson(value.at("behavior"));
65 if (!behavior.ok())
66 return behavior.status();
67 asset.behavior = std::move(*behavior);
68 }
69 if (graphics_version) {
70 const auto& sources = value.at("graphics_sources");
71 if (!sources.is_array() || sources.size() != 8)
72 return invalid();
73 for (size_t i = 0; i < 8; ++i) {
74 const auto& json = sources[i];
75 if (!json.is_object() || json.size() != 4 ||
76 !json.at("offset").is_number_integer() || json.at("offset") < 0 ||
77 json.at("offset") > std::numeric_limits<uint32_t>::max() ||
78 !json.at("bpp").is_number_integer() || json.at("bpp") < 2 ||
79 json.at("bpp") > 4)
80 return invalid();
81 auto& source = asset.graphics_sources[i];
82 source.kind = json.at("kind").get<std::string>();
83 source.path = json.at("path").get<std::string>();
84 source.offset = json.at("offset").get<uint32_t>();
85 source.bpp = json.at("bpp").get<int>();
86 if ((source.kind != "sheet" && source.kind != "bin" &&
87 source.kind != "raw_rom") ||
88 source.bpp < 2 || source.bpp > 4 || source.path.size() > 4096 ||
89 source.path.find('\0') != std::string::npos ||
90 (source.kind == "bin" && source.path.empty()) ||
91 (source.kind != "bin" && !source.path.empty()) ||
92 (source.kind == "sheet" && (source.offset || source.bpp != 3)))
93 return invalid();
94 }
95 }
96 asset.zsm_path = value.at("zsm_path").get<std::string>();
97 asset.catalog_key = value.at("catalog_key").get<std::string>();
98 asset.draw_adapter = value.at("draw_adapter").get<std::string>();
99 asset.source_path = value.at("source_path").get<std::string>();
100 asset.source_label = value.at("source_label").get<std::string>();
101 asset.source_sha256 = value.at("source_sha256").get<std::string>();
102 if (asset.zsm_path.empty() ||
103 asset.zsm_path.find('\0') != std::string::npos ||
104 (asset.draw_adapter != "literal_v1" &&
105 asset.draw_adapter != "oracle_maple_v1"))
106 return invalid();
107 if (!asset.source_path.empty()) {
108 if (asset.source_label.empty() || asset.source_sha256.size() != 64 ||
109 asset.source_sha256.find_first_not_of("0123456789abcdef") !=
110 std::string::npos)
111 return invalid();
112 } else if (!asset.source_label.empty() || !asset.source_sha256.empty()) {
113 return invalid();
114 }
115 const auto& sheets = value.at("sheets");
116 const auto& rows = value.at("palette_rows");
117 if (!sheets.is_array() || sheets.size() != 8 || !rows.is_array() ||
118 rows.size() != 8)
119 return invalid();
120 for (size_t i = 0; i < 8; ++i) {
121 if (!sheets[i].is_number_integer() || sheets[i] < 0 || sheets[i] > 255)
122 return invalid();
123 asset.sheets[i] = sheets[i].get<int>();
124 if (!rows[i].is_object() || rows[i].size() != 2 ||
125 !rows[i].at("index").is_number_integer() || rows[i].at("index") < 0 ||
126 rows[i].at("index") > 255)
127 return invalid();
128 auto& row = asset.palette_rows[i];
129 row.group = rows[i].at("group").get<std::string>();
130 row.index = rows[i].at("index").get<int>();
131 if (row.group != "auto" && row.group != "global_sprites" &&
132 row.group != "sprites_aux1" && row.group != "sprites_aux2" &&
133 row.group != "sprites_aux3")
134 return invalid();
135 }
136 return asset;
137 } catch (const nlohmann::json::exception&) {
138 return invalid();
139 }
140}
141
142inline absl::StatusOr<SpriteAssetBinding> ParseSpriteAssetBinding(
143 const std::string& text) {
144 if (text.size() > 65536)
145 return absl::ResourceExhaustedError("Sprite asset binding exceeds 64 KiB");
146 try {
147 std::vector<std::set<std::string>> keys;
148 auto value = nlohmann::json::parse(
149 text, [&](int depth, nlohmann::json::parse_event_t event,
150 nlohmann::json& node) {
151 if (depth > 16)
152 throw std::runtime_error("Sprite asset nesting exceeds limit");
153 if (event == nlohmann::json::parse_event_t::object_start)
154 keys.emplace_back();
155 else if (event == nlohmann::json::parse_event_t::key) {
156 if (!keys.back().insert(node.get<std::string>()).second)
157 throw std::runtime_error("Duplicate sprite asset field");
158 } else if (event == nlohmann::json::parse_event_t::object_end)
159 keys.pop_back();
160 return true;
161 });
162 return SpriteAssetFromJson(value);
163 } catch (const std::exception& e) {
164 return absl::InvalidArgumentError(e.what());
165 }
166}
167} // namespace yaze::project
168#endif
absl::StatusOr< SpriteBehavior > SpriteBehaviorFromJson(const nlohmann::json &json)
nlohmann::json SpriteBehaviorToJson(const SpriteBehavior &behavior)
absl::StatusOr< SpriteAssetBinding > ParseSpriteAssetBinding(const std::string &text)
nlohmann::json SpriteAssetToJson(const SpriteAssetBinding &asset)
absl::StatusOr< SpriteAssetBinding > SpriteAssetFromJson(const nlohmann::json &value)
std::array< uint8_t, 8 > sheets
std::array< SpritePaletteBinding, 8 > palette_rows
std::array< SpriteGraphicsSource, 8 > graphics_sources