From bed7e4b892a2e505590f76aefdcb64b81c4a6ceb Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 11:09:59 -0700 Subject: [PATCH] feat(editor): --validate --json for machine-readable zone scoring Mirrors --info-extract --json. Schema: { "zone": "custom_zones/Foo", "score": 3, "maxScore": 7, "formats": "WOT WHM zone.json ", "wot": { "present": true, "count": 1, "valid": true }, "whm": { "present": true, "count": 1, "valid": true }, "wom": { "present": false, "count": 0, "valid": false }, "wob": { ... }, "woc": { ... }, "png": { "present": true, "count": 12 }, "zoneJson": true, "creatures": false, "quests": false, "objects": false } Exit code is still 0 if score == 7 (full open coverage), 1 otherwise, so CI gates work the same way: if ! wowee_editor --validate "$zone" --json | jq -e '.score == 7'; then echo "zone incomplete"; exit 1 fi --- tools/editor/main.cpp | 36 +++++++++++++++++++++++++++++++++++- 1 file changed, 35 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 267b5590..5bccb3d4 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -36,7 +36,8 @@ static void printUsage(const char* argv0) { std::printf(" --regen-collision Rebuild every WOC under a zone dir and exit\n"); std::printf(" --fix-zone Re-parse + re-save zone JSONs to apply latest scrubs/caps and exit\n"); std::printf(" --export-png Render heightmap, normal-map, and zone-map PNG previews\n"); - std::printf(" --validate Score zone open-format completeness and exit\n"); + std::printf(" --validate [--json]\n"); + std::printf(" Score zone open-format completeness and exit\n"); std::printf(" --zone-summary One-shot validate + creature/object/quest counts and exit\n"); std::printf(" --info Print WOM file metadata (version, counts) and exit\n"); std::printf(" --info-wob Print WOB building metadata (groups, portals, doodads) and exit\n"); @@ -622,8 +623,41 @@ int main(int argc, char* argv[]) { return v.openFormatScore() == 7 ? 0 : 1; } else if (std::strcmp(argv[i], "--validate") == 0 && i + 1 < argc) { std::string zoneDir = argv[++i]; + // Optional --json after the dir for machine-readable output + // (matches --info-extract --json). + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; auto v = wowee::editor::ContentPacker::validateZone(zoneDir); int score = v.openFormatScore(); + if (jsonOut) { + nlohmann::json j; + j["zone"] = zoneDir; + j["score"] = score; + j["maxScore"] = 7; + j["formats"] = v.summary(); + auto fmt = [&](const char* name, bool present, int count, + bool valid = true, int invalid = 0) { + nlohmann::json f; + f["present"] = present; + f["count"] = count; + f["valid"] = valid; + if (invalid > 0) f["invalid"] = invalid; + j[name] = f; + }; + fmt("wot", v.hasWot, v.wotCount); + fmt("whm", v.hasWhm, v.whmCount, v.whmValid); + fmt("wom", v.hasWom, v.womCount, v.womValid, v.womInvalidCount); + fmt("wob", v.hasWob, v.wobCount, v.wobValid, v.wobInvalidCount); + fmt("woc", v.hasWoc, v.wocCount, v.wocValid, v.wocInvalidCount); + fmt("png", v.hasPng, v.pngCount); + j["zoneJson"] = v.hasZoneJson; + j["creatures"] = v.hasCreatures; + j["quests"] = v.hasQuests; + j["objects"] = v.hasObjects; + std::printf("%s\n", j.dump(2).c_str()); + return score == 7 ? 0 : 1; + } std::printf("Zone: %s\n", zoneDir.c_str()); std::printf("Open format score: %d/7\n", score); std::printf("Formats: %s\n", v.summary().c_str());