yaze 0.3.2
Link to the Past ROM Editor
 
Loading...
Searching...
No Matches
resource_catalog.cc
Go to the documentation of this file.
2
3#include <sstream>
4
5#include "absl/strings/str_cat.h"
6#include "absl/strings/str_join.h"
7#include "absl/strings/string_view.h"
8
9namespace yaze {
10namespace cli {
11
12namespace {
13
15 ResourceSchema schema;
16 schema.resource = "palette";
17 schema.description =
18 "Palette manipulation commands covering export, import, and color "
19 "editing.";
20
21 ResourceAction export_action;
22 export_action.name = "export";
23 export_action.synopsis =
24 "z3ed palette export --group <group> --id <id> --to <file>";
25 export_action.stability = "experimental";
26 export_action.arguments = {
27 ResourceArgument{"--group", "integer", true, "Palette group id (0-31)."},
28 ResourceArgument{"--id", "integer", true,
29 "Palette index inside the group."},
30 ResourceArgument{"--to", "path", true,
31 "Destination file path for binary export."},
32 };
33 export_action.effects = {
34 "Reads ROM palette buffer and writes binary palette data to disk."};
35
36 ResourceAction import_action;
37 import_action.name = "import";
38 import_action.synopsis =
39 "z3ed palette import --group <group> --id <id> --from <file>";
40 import_action.stability = "experimental";
41 import_action.arguments = {
42 ResourceArgument{"--group", "integer", true, "Palette group id (0-31)."},
43 ResourceArgument{"--id", "integer", true,
44 "Palette index inside the group."},
45 ResourceArgument{"--from", "path", true, "Source binary palette file."},
46 };
47 import_action.effects = {
48 "Writes imported palette bytes into ROM buffer and marks project dirty."};
49
50 schema.actions = {export_action, import_action};
51 return schema;
52}
53
55 ResourceSchema schema;
56 schema.resource = "rom";
57 schema.description = "ROM validation, diffing, and snapshot helpers.";
58
59 ResourceAction info_action;
60 info_action.name = "info";
61 info_action.synopsis = "z3ed rom info --rom <file>";
62 info_action.stability = "stable";
63 info_action.arguments = {
64 ResourceArgument{"--rom", "path", true,
65 "Path to ROM file configured via global flag."},
66 };
67 info_action.effects = {
68 "Reads ROM from disk and displays basic information (title, size, "
69 "filename)."};
70 info_action.returns = {{"title", "string", "ROM internal title from header."},
71 {"size", "integer", "ROM file size in bytes."},
72 {"filename", "string", "Full path to the ROM file."}};
73
74 ResourceAction validate_action;
75 validate_action.name = "validate";
76 validate_action.synopsis = "z3ed rom validate --rom <file>";
77 validate_action.stability = "stable";
78 validate_action.arguments = {
79 ResourceArgument{"--rom", "path", true,
80 "Path to ROM file configured via global flag."},
81 };
82 validate_action.effects = {
83 "Reads ROM from disk, verifies checksum, and reports header status."};
84 validate_action.returns = {
85 {"report", "object",
86 "Structured validation summary with checksum and header results."}};
87
88 ResourceAction diff_action;
89 diff_action.name = "diff";
90 diff_action.synopsis = "z3ed rom diff <rom_a> <rom_b>";
91 diff_action.stability = "beta";
92 diff_action.arguments = {
93 ResourceArgument{"rom_a", "path", true, "Reference ROM path."},
94 ResourceArgument{"rom_b", "path", true, "Candidate ROM path."},
95 };
96 diff_action.effects = {
97 "Reads two ROM images, compares bytes, and streams differences to "
98 "stdout."};
99 diff_action.returns = {
100 {"differences", "integer", "Count of mismatched bytes between ROMs."}};
101
102 ResourceAction generate_action;
103 generate_action.name = "generate-golden";
104 generate_action.synopsis =
105 "z3ed rom generate-golden <rom_file> <golden_file>";
106 generate_action.stability = "experimental";
107 generate_action.arguments = {
108 ResourceArgument{"rom_file", "path", true, "Source ROM to snapshot."},
109 ResourceArgument{"golden_file", "path", true,
110 "Output path for golden image."},
111 };
112 generate_action.effects = {
113 "Writes out exact ROM image for tooling baselines and diff workflows."};
114 generate_action.returns = {
115 {"artifact", "path", "Absolute path to the generated golden image."}};
116
117 schema.actions = {info_action, validate_action, diff_action, generate_action};
118 return schema;
119}
120
122 ResourceSchema schema;
123 schema.resource = "patch";
124 schema.description =
125 "Patch authoring and application commands covering BPS and Asar flows.";
126
127 ResourceAction apply_action;
128 apply_action.name = "apply";
129 apply_action.synopsis = "z3ed patch apply <rom_file> <bps_patch>";
130 apply_action.stability = "beta";
131 apply_action.arguments = {
132 ResourceArgument{"rom_file", "path", true,
133 "Source ROM image that will receive the patch."},
134 ResourceArgument{"bps_patch", "path", true,
135 "BPS patch to apply to the ROM."},
136 };
137 apply_action.effects = {
138 "Loads ROM from disk, applies a BPS patch, and writes `patched.sfc`."};
139 apply_action.returns = {
140 {"artifact", "path",
141 "Absolute path to the patched ROM image produced on success."}};
142
143 ResourceAction asar_action;
144 asar_action.name = "apply-asar";
145 asar_action.synopsis = "z3ed patch apply-asar <patch.asm>";
146 asar_action.stability = "prototype";
147 asar_action.arguments = {
148 ResourceArgument{"patch.asm", "path", true,
149 "Assembly patch consumed by the bundled Asar runtime."},
150 ResourceArgument{"--rom", "path", false,
151 "ROM path supplied via global --rom flag."},
152 };
153 asar_action.effects = {
154 "Invokes Asar against the active ROM buffer and applies assembled "
155 "changes."};
156 asar_action.returns = {
157 {"log", "string", "Assembler diagnostics emitted during application."}};
158
159 ResourceAction create_action;
160 create_action.name = "create";
161 create_action.synopsis =
162 "z3ed patch create --source <rom> --target <rom> --out <patch.bps>";
163 create_action.stability = "experimental";
164 create_action.arguments = {
165 ResourceArgument{"--source", "path", true,
166 "Baseline ROM used when computing the patch."},
167 ResourceArgument{"--target", "path", true,
168 "Modified ROM to diff against the baseline."},
169 ResourceArgument{"--out", "path", true,
170 "Output path for the generated BPS patch."},
171 };
172 create_action.effects = {
173 "Compares source and target images to synthesize a distributable BPS "
174 "patch."};
175 create_action.returns = {
176 {"artifact", "path", "File system path to the generated patch."}};
177
178 schema.actions = {apply_action, asar_action, create_action};
179 return schema;
180}
181
183 ResourceSchema schema;
184 schema.resource = "overworld";
185 schema.description = "Overworld tile inspection and manipulation commands.";
186
187 ResourceAction get_tile;
188 get_tile.name = "get-tile";
189 get_tile.synopsis = "z3ed overworld get-tile --map <map_id> --x <x> --y <y>";
190 get_tile.stability = "stable";
191 get_tile.arguments = {
192 ResourceArgument{"--map", "integer", true,
193 "Overworld map identifier (0-63)."},
194 ResourceArgument{"--x", "integer", true, "Tile x coordinate."},
195 ResourceArgument{"--y", "integer", true, "Tile y coordinate."},
196 };
197 get_tile.returns = {
198 {"tile", "integer", "Tile id located at the supplied coordinates."}};
199
200 ResourceAction set_tile;
201 set_tile.name = "set-tile";
202 set_tile.synopsis =
203 "z3ed overworld set-tile --map <map_id> --x <x> --y <y> --tile <tile_id>";
204 set_tile.stability = "experimental";
205 set_tile.arguments = {
206 ResourceArgument{"--map", "integer", true,
207 "Overworld map identifier (0-63)."},
208 ResourceArgument{"--x", "integer", true, "Tile x coordinate."},
209 ResourceArgument{"--y", "integer", true, "Tile y coordinate."},
210 ResourceArgument{"--tile", "integer", true, "Tile id to write."},
211 };
212 set_tile.effects = {
213 "Mutates overworld tile map and enqueues render invalidation."};
214
215 schema.actions = {get_tile, set_tile};
216 return schema;
217}
218
220 ResourceSchema schema;
221 schema.resource = "dungeon";
222 schema.description = "Dungeon room export and inspection utilities.";
223
224 ResourceAction export_action;
225 export_action.name = "export";
226 export_action.synopsis = "z3ed dungeon export <room_id>";
227 export_action.stability = "prototype";
228 export_action.arguments = {
229 ResourceArgument{"room_id", "integer", true,
230 "Dungeon room identifier to inspect."},
231 };
232 export_action.effects = {
233 "Loads the active ROM via --rom and prints metadata for the requested "
234 "room."};
235 export_action.returns = {{"metadata", "object",
236 "Structured room summary including blockset, "
237 "spriteset, palette, and layout."}};
238
239 ResourceAction list_objects_action;
240 list_objects_action.name = "list-objects";
241 list_objects_action.synopsis = "z3ed dungeon list-objects <room_id>";
242 list_objects_action.stability = "prototype";
243 list_objects_action.arguments = {
245 "room_id", "integer", true,
246 "Dungeon room identifier whose objects should be listed."},
247 };
248 list_objects_action.effects = {
249 "Streams parsed dungeon object records for the requested room to "
250 "stdout."};
251 list_objects_action.returns = {
252 {"objects", "array",
253 "Collection of tile object records with ids, coordinates, and layers."}};
254
255 schema.actions = {export_action, list_objects_action};
256 return schema;
257}
258
260 ResourceSchema schema;
261 schema.resource = "agent";
262 schema.description =
263 "Agent workflow helpers including planning, diffing, listing, and schema "
264 "discovery.";
265
266 ResourceAction describe_action;
267 describe_action.name = "describe";
268 describe_action.synopsis = "z3ed agent describe --resource <name>";
269 describe_action.stability = "prototype";
270 describe_action.arguments = {
271 ResourceArgument{"--resource", "string", false,
272 "Optional resource name to filter results."},
273 };
274 describe_action.returns = {
275 {"schema", "object",
276 "JSON schema describing resource arguments and semantics."}};
277
278 ResourceAction list_action;
279 list_action.name = "list";
280 list_action.synopsis = "z3ed agent list";
281 list_action.stability = "prototype";
282 list_action.arguments = {};
283 list_action.effects = {{"reads", "proposal_registry"}};
284 list_action.returns = {
285 {"proposals", "array",
286 "List of all proposals with ID, status, prompt, and metadata."}};
287
288 ResourceAction diff_action;
289 diff_action.name = "diff";
290 diff_action.synopsis = "z3ed agent diff [--proposal-id <id>]";
291 diff_action.stability = "prototype";
292 diff_action.arguments = {
293 ResourceArgument{"--proposal-id", "string", false,
294 "Optional proposal ID to view specific proposal. "
295 "Defaults to latest pending."},
296 };
297 diff_action.effects = {{"reads", "proposal_registry"}, {"reads", "sandbox"}};
298 diff_action.returns = {
299 {"diff", "string", "Unified diff showing changes to ROM."},
300 {"log", "string", "Execution log of commands run."},
301 {"metadata", "object",
302 "Proposal metadata including status and timestamps."}};
303
304 schema.actions = {describe_action, list_action, diff_action};
305 return schema;
306}
307
308} // namespace
309
311 static ResourceCatalog* instance = new ResourceCatalog();
312 return *instance;
313}
314
316 : resources_({MakeRomSchema(), MakePatchSchema(), MakePaletteSchema(),
317 MakeOverworldSchema(), MakeDungeonSchema(),
318 MakeAgentSchema()}) {}
319
320absl::StatusOr<ResourceSchema> ResourceCatalog::GetResource(
321 absl::string_view name) const {
322 for (const auto& resource : resources_) {
323 if (resource.resource == name) {
324 return resource;
325 }
326 }
327 return absl::NotFoundError(absl::StrCat("Resource not found: ", name));
328}
329
330const std::vector<ResourceSchema>& ResourceCatalog::AllResources() const {
331 return resources_;
332}
333
335 const ResourceSchema& schema) const {
336 return SerializeResources({schema});
337}
338
340 const std::vector<ResourceSchema>& schemas) const {
341 std::vector<std::string> entries;
342 entries.reserve(schemas.size());
343 for (const auto& resource : schemas) {
344 std::vector<std::string> action_entries;
345 action_entries.reserve(resource.actions.size());
346 for (const auto& action : resource.actions) {
347 std::vector<std::string> arg_entries;
348 arg_entries.reserve(action.arguments.size());
349 for (const auto& arg : action.arguments) {
350 arg_entries.push_back(absl::StrCat(
351 "{\"flag\":\"", EscapeJson(arg.flag), "\",\"type\":\"",
352 EscapeJson(arg.type),
353 "\",\"required\":", arg.required ? "true" : "false",
354 ",\"description\":\"", EscapeJson(arg.description), "\"}"));
355 }
356 std::vector<std::string> effect_entries;
357 effect_entries.reserve(action.effects.size());
358 for (const auto& effect : action.effects) {
359 effect_entries.push_back(absl::StrCat("\"", EscapeJson(effect), "\""));
360 }
361 std::vector<std::string> return_entries;
362 return_entries.reserve(action.returns.size());
363 for (const auto& ret : action.returns) {
364 return_entries.push_back(absl::StrCat(
365 "{\"field\":\"", EscapeJson(ret.field), "\",\"type\":\"",
366 EscapeJson(ret.type), "\",\"description\":\"",
367 EscapeJson(ret.description), "\"}"));
368 }
369 action_entries.push_back(absl::StrCat(
370 "{\"name\":\"", EscapeJson(action.name), "\",\"synopsis\":\"",
371 EscapeJson(action.synopsis), "\",\"stability\":\"",
372 EscapeJson(action.stability), "\",\"arguments\":[",
373 absl::StrJoin(arg_entries, ","), "],", "\"effects\":[",
374 absl::StrJoin(effect_entries, ","), "],", "\"returns\":[",
375 absl::StrJoin(return_entries, ","), "]}"));
376 }
377 entries.push_back(absl::StrCat(
378 "{\"resource\":\"", EscapeJson(resource.resource),
379 "\",\"description\":\"", EscapeJson(resource.description),
380 "\",\"actions\":[", absl::StrJoin(action_entries, ","), "]}"));
381 }
382 return absl::StrCat("{\"resources\":[", absl::StrJoin(entries, ","), "]}");
383}
384
385std::string ResourceCatalog::EscapeJson(absl::string_view value) {
386 std::string out;
387 out.reserve(value.size());
388 for (char c : value) {
389 switch (c) {
390 case '\\':
391 out += "\\\\";
392 break;
393 case '\"':
394 out += "\\\"";
395 break;
396 case '\n':
397 out += "\\n";
398 break;
399 case '\r':
400 out += "\\r";
401 break;
402 case '\t':
403 out += "\\t";
404 break;
405 default:
406 out += c;
407 break;
408 }
409 }
410 return out;
411}
412
414 const std::vector<ResourceSchema>& schemas, absl::string_view version,
415 absl::string_view last_updated) const {
416 std::string out;
417 absl::StrAppend(&out, "# Auto-generated resource catalogue\n");
418 absl::StrAppend(&out, "version: ", EscapeYaml(version), "\n");
419 absl::StrAppend(&out, "last_updated: ", EscapeYaml(last_updated), "\n");
420 absl::StrAppend(&out, "resources:\n");
421
422 for (const auto& resource : schemas) {
423 absl::StrAppend(&out, " - name: ", EscapeYaml(resource.resource), "\n");
424 absl::StrAppend(&out, " description: ", EscapeYaml(resource.description),
425 "\n");
426
427 if (resource.actions.empty()) {
428 absl::StrAppend(&out, " actions: []\n");
429 continue;
430 }
431
432 absl::StrAppend(&out, " actions:\n");
433 for (const auto& action : resource.actions) {
434 absl::StrAppend(&out, " - name: ", EscapeYaml(action.name), "\n");
435 absl::StrAppend(&out, " synopsis: ", EscapeYaml(action.synopsis),
436 "\n");
437 absl::StrAppend(&out, " stability: ", EscapeYaml(action.stability),
438 "\n");
439
440 if (action.arguments.empty()) {
441 absl::StrAppend(&out, " args: []\n");
442 } else {
443 absl::StrAppend(&out, " args:\n");
444 for (const auto& arg : action.arguments) {
445 absl::StrAppend(&out, " - flag: ", EscapeYaml(arg.flag),
446 "\n");
447 absl::StrAppend(&out, " type: ", EscapeYaml(arg.type),
448 "\n");
449 absl::StrAppend(&out, " required: ",
450 arg.required ? "true\n" : "false\n");
451 absl::StrAppend(&out, " description: ",
452 EscapeYaml(arg.description), "\n");
453 }
454 }
455
456 if (action.effects.empty()) {
457 absl::StrAppend(&out, " effects: []\n");
458 } else {
459 absl::StrAppend(&out, " effects:\n");
460 for (const auto& effect : action.effects) {
461 absl::StrAppend(&out, " - ", EscapeYaml(effect), "\n");
462 }
463 }
464
465 if (action.returns.empty()) {
466 absl::StrAppend(&out, " returns: []\n");
467 } else {
468 absl::StrAppend(&out, " returns:\n");
469 for (const auto& ret : action.returns) {
470 absl::StrAppend(&out, " - field: ", EscapeYaml(ret.field),
471 "\n");
472 absl::StrAppend(&out, " type: ", EscapeYaml(ret.type),
473 "\n");
474 absl::StrAppend(&out, " description: ",
475 EscapeYaml(ret.description), "\n");
476 }
477 }
478 }
479 }
480
481 return out;
482}
483
484std::string ResourceCatalog::EscapeYaml(absl::string_view value) {
485 std::string out;
486 out.reserve(value.size() + 2);
487 out.push_back('"');
488 for (char c : value) {
489 switch (c) {
490 case '\\':
491 out += "\\\\";
492 break;
493 case '"':
494 out += "\\\"";
495 break;
496 case '\n':
497 out += "\\n";
498 break;
499 case '\r':
500 out += "\\r";
501 break;
502 case '\t':
503 out += "\\t";
504 break;
505 default:
506 out.push_back(c);
507 break;
508 }
509 }
510 out.push_back('"');
511 return out;
512}
513
514} // namespace cli
515} // namespace yaze
static std::string EscapeJson(absl::string_view value)
absl::StatusOr< ResourceSchema > GetResource(absl::string_view name) const
static const ResourceCatalog & Instance()
std::string SerializeResourcesAsYaml(const std::vector< ResourceSchema > &schemas, absl::string_view version, absl::string_view last_updated) const
const std::vector< ResourceSchema > & AllResources() const
std::string SerializeResources(const std::vector< ResourceSchema > &schemas) const
std::string SerializeResource(const ResourceSchema &schema) const
std::vector< ResourceSchema > resources_
static std::string EscapeYaml(absl::string_view value)
std::vector< ResourceArgument > arguments
std::vector< ReturnValue > returns
std::vector< std::string > effects
std::vector< ResourceAction > actions