130 static absl::StatusOr<GraphicsScratchLibrary>
Parse(
const std::string& text) {
131 if (text.size() > 4 * 1024 * 1024)
132 return absl::ResourceExhaustedError(
"Scratch library exceeds 4 MiB");
134 const auto root = nlohmann::json::parse(text);
135 if (!root.is_object() || root.at(
"version") != 1 ||
136 !root.at(
"assets").is_array() ||
138 return absl::InvalidArgumentError(
"Unsupported scratch library");
140 std::set<uint64_t> ids;
141 for (
const auto& entry : root.at(
"assets")) {
142 if (!entry.is_object() ||
143 entry.size() != 6 + (entry.contains(
"bin_source") ? 1 : 0))
144 return absl::InvalidArgumentError(
"Invalid scratch asset fields");
146 const auto& pixels = entry.at(
"pixels");
147 const auto& palette = entry.at(
"palette");
148 if (!pixels.is_array() || pixels.size() != 4096 ||
149 !palette.is_array() || palette.size() != 16 ||
150 !entry.at(
"id").is_number_unsigned())
151 return absl::InvalidArgumentError(
"Invalid scratch asset shape");
152 asset.
id = entry.at(
"id").get<uint64_t>();
153 asset.
name = entry.at(
"name").get<std::string>();
154 asset.
source = entry.at(
"source").get<std::string>();
155 if (!entry.at(
"bpp").is_number_integer() || entry.at(
"bpp") < 2 ||
157 return absl::InvalidArgumentError(
"Invalid scratch bit depth");
158 asset.
bpp = entry.at(
"bpp").get<
int>();
159 if (entry.contains(
"bin_source")) {
160 const auto& source = entry.at(
"bin_source");
161 if (!source.is_object() || source.size() != 3 ||
162 !source.at(
"offset").is_number_unsigned() ||
163 !source.at(
"original_chunk").is_array() ||
164 source.at(
"original_chunk").size() !=
165 static_cast<size_t>(64 * 8 * asset.
bpp))
166 return absl::InvalidArgumentError(
"Invalid scratch BIN provenance");
167 asset.
source_path = source.at(
"path").get<std::string>();
170 return absl::InvalidArgumentError(
"Empty scratch BIN source path");
171 for (
const auto&
byte : source.at(
"original_chunk")) {
172 if (!
byte.is_number_integer() || byte < 0 || byte > 255)
173 return absl::InvalidArgumentError(
174 "Invalid scratch BIN source byte");
178 for (
size_t i = 0; i < pixels.size(); ++i) {
179 if (!pixels[i].is_number_integer() || pixels[i].get<int64_t>() < 0 ||
180 pixels[i].get<int64_t>() > 15)
181 return absl::InvalidArgumentError(
"Invalid scratch pixel");
182 asset.
pixels[i] = pixels[i].get<uint8_t>();
184 for (
size_t i = 0; i < palette.size(); ++i) {
185 if (!palette[i].is_number_integer() ||
186 palette[i].get<int64_t>() < 0 ||
187 palette[i].get<int64_t>() > 0x7fff)
188 return absl::InvalidArgumentError(
"Invalid scratch palette");
189 asset.
palette[i] = palette[i].get<uint16_t>();
191 if (
auto status = asset.
Validate(); !status.ok())
193 if (asset.
id == 0 || asset.
id == UINT64_MAX ||
194 !ids.insert(asset.
id).second)
195 return absl::InvalidArgumentError(
196 "Invalid or duplicate scratch asset ID");
198 library.
assets.push_back(std::move(asset));
200 if (!root.at(
"next_id").is_number_unsigned())
201 return absl::InvalidArgumentError(
"Invalid scratch ID sequence");
203 std::max(library.
next_id, root.at(
"next_id").get<uint64_t>());
205 }
catch (
const nlohmann::json::exception&) {
206 return absl::InvalidArgumentError(
"Invalid scratch library JSON");