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