From 39a9f224a0cc3f239249fea089a3a6311e3dffac Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 11:19:29 -0700 Subject: [PATCH] feat(editor): --diff-wcp --json for machine-readable archive diff Sixth and last commonly-used inspector to gain --json mode. Schema: { "a": "path/to/A.wcp", "b": "path/to/B.wcp", "identical": 1, "changed": 0, "onlyA": 2, "onlyB": 2, "changedFiles": [ {"path": "foo.dbc", "aSize": 1024, "bSize": 1280} ], "onlyAFiles": ["Da_30_30.whm", "Da_30_30.wot"], "onlyBFiles": ["Db_31_31.whm", "Db_31_31.wot"] } Exit code matches the human path: 0 if identical, 1 otherwise. Lets CI verify two builds produce byte-identical archives: if ! wowee_editor --diff-wcp old.wcp new.wcp --json | jq -e \ '.changed == 0 and .onlyA == 0 and .onlyB == 0'; then echo "WCP layout drift detected"; exit 1 fi --- tools/editor/main.cpp | 34 +++++++++++++++++++++++++++++++++- 1 file changed, 33 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index fc81dcf1..13885b14 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -53,7 +53,8 @@ static void printUsage(const char* argv0) { std::printf(" --info-wcp [--json]\n"); std::printf(" Print WCP archive metadata (name, files) and exit\n"); std::printf(" --list-wcp Print every file inside a WCP archive (sorted by path) and exit\n"); - std::printf(" --diff-wcp Compare two WCPs file-by-file; exit 0 if identical, 1 otherwise\n"); + std::printf(" --diff-wcp [--json]\n"); + std::printf(" Compare two WCPs file-by-file; exit 0 if identical, 1 otherwise\n"); std::printf(" --pack-wcp [dst] Pack a zone dir/name into a .wcp archive and exit\n"); std::printf(" --unpack-wcp [dst] Extract a WCP archive (default dst=custom_zones/) and exit\n"); std::printf(" --version Show version and format info\n\n"); @@ -415,6 +416,10 @@ int main(int argc, char* argv[]) { // across editor versions for regression detection. std::string aPath = argv[++i]; std::string bPath = argv[++i]; + // Optional --json after both paths for machine-readable output. + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; wowee::editor::ContentPackInfo aInfo, bInfo; if (!wowee::editor::ContentPacker::readInfo(aPath, aInfo) || !wowee::editor::ContentPacker::readInfo(bPath, bInfo)) { @@ -427,6 +432,10 @@ int main(int argc, char* argv[]) { int onlyA = 0, onlyB = 0, sizeChanged = 0, identical = 0; std::vector onlyAList, onlyBList, changedList; + // For JSON we want size-change rows as structured records, not + // pre-formatted strings — collect both forms in one pass. + struct ChangedRow { std::string path; uint64_t aSize, bSize; }; + std::vector changedRows; for (const auto& [p, sz] : aFiles) { auto it = bFiles.find(p); if (it == bFiles.end()) { onlyA++; onlyAList.push_back(p); } @@ -434,6 +443,7 @@ int main(int argc, char* argv[]) { sizeChanged++; changedList.push_back(p + " (" + std::to_string(sz) + " -> " + std::to_string(it->second) + ")"); + changedRows.push_back({p, sz, it->second}); } else identical++; } for (const auto& [p, sz] : bFiles) { @@ -442,6 +452,28 @@ int main(int argc, char* argv[]) { std::sort(onlyAList.begin(), onlyAList.end()); std::sort(onlyBList.begin(), onlyBList.end()); std::sort(changedList.begin(), changedList.end()); + if (jsonOut) { + nlohmann::json j; + j["a"] = aPath; + j["b"] = bPath; + j["identical"] = identical; + j["changed"] = sizeChanged; + j["onlyA"] = onlyA; + j["onlyB"] = onlyB; + std::sort(changedRows.begin(), changedRows.end(), + [](const auto& x, const auto& y) { return x.path < y.path; }); + nlohmann::json changedArr = nlohmann::json::array(); + for (const auto& c : changedRows) { + changedArr.push_back({{"path", c.path}, + {"aSize", c.aSize}, + {"bSize", c.bSize}}); + } + j["changedFiles"] = changedArr; + j["onlyAFiles"] = onlyAList; + j["onlyBFiles"] = onlyBList; + std::printf("%s\n", j.dump(2).c_str()); + return (onlyA + onlyB + sizeChanged) == 0 ? 0 : 1; + } std::printf("Diff: %s vs %s\n", aPath.c_str(), bPath.c_str()); std::printf(" identical : %d\n", identical); std::printf(" changed : %d\n", sizeChanged);