diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 1d012b5e..b579a15b 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -620,6 +620,8 @@ static void printUsage(const char* argv0) { std::printf(" Compare two WHM/WOT terrain pairs (chunks, height range, placements)\n"); std::printf(" --diff-woc [--json]\n"); std::printf(" Compare two WOC collision meshes (triangles, walkable/steep counts, tile)\n"); + std::printf(" --diff-jsondbc [--json]\n"); + std::printf(" Compare two JSON DBC sidecars (format/source/recordCount/fieldCount)\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(" --list-commands Print every recognized --flag, one per line, and exit\n"); @@ -719,6 +721,11 @@ int main(int argc, char* argv[]) { "--diff-woc requires \n"); return 1; } + if (std::strcmp(argv[i], "--diff-jsondbc") == 0 && i + 2 >= argc) { + std::fprintf(stderr, + "--diff-jsondbc requires \n"); + return 1; + } if (std::strcmp(argv[i], "--diff-wcp") == 0 && i + 2 >= argc) { std::fprintf(stderr, "--diff-wcp requires two paths\n"); return 1; @@ -3453,6 +3460,83 @@ int main(int argc, char* argv[]) { return 0; } return 1; + } else if (std::strcmp(argv[i], "--diff-jsondbc") == 0 && i + 2 < argc) { + // JSON DBC structural diff. Catches schema regressions when + // a sidecar is regenerated by a different tool version (or + // a different DBC layout produces a different field count). + std::string aPath = argv[++i]; + std::string bPath = argv[++i]; + bool jsonOut = (i + 1 < argc && + std::strcmp(argv[i + 1], "--json") == 0); + if (jsonOut) i++; + auto loadDoc = [](const std::string& p, nlohmann::json& doc) { + std::ifstream in(p); + if (!in) return false; + try { in >> doc; } catch (...) { return false; } + return true; + }; + nlohmann::json a, b; + if (!loadDoc(aPath, a)) { + std::fprintf(stderr, "diff-jsondbc: failed to read %s\n", aPath.c_str()); + return 1; + } + if (!loadDoc(bPath, b)) { + std::fprintf(stderr, "diff-jsondbc: failed to read %s\n", bPath.c_str()); + return 1; + } + // Pull comparable fields with safe defaults. + std::string aFmt = a.value("format", std::string{}); + std::string bFmt = b.value("format", std::string{}); + std::string aSrc = a.value("source", std::string{}); + std::string bSrc = b.value("source", std::string{}); + uint32_t aRC = a.value("recordCount", 0u); + uint32_t bRC = b.value("recordCount", 0u); + uint32_t aFC = a.value("fieldCount", 0u); + uint32_t bFC = b.value("fieldCount", 0u); + uint32_t aActual = (a.contains("records") && a["records"].is_array()) + ? static_cast(a["records"].size()) : 0u; + uint32_t bActual = (b.contains("records") && b["records"].is_array()) + ? static_cast(b["records"].size()) : 0u; + int diffs = 0; + if (aFmt != bFmt) diffs++; + if (aSrc != bSrc) diffs++; + if (aRC != bRC) diffs++; + if (aFC != bFC) diffs++; + if (aActual != bActual) diffs++; + if (jsonOut) { + nlohmann::json j; + j["a"] = aPath; j["b"] = bPath; + j["format"] = {{"a", aFmt}, {"b", bFmt}}; + j["source"] = {{"a", aSrc}, {"b", bSrc}}; + j["recordCount"] = {{"a", aRC}, {"b", bRC}}; + j["fieldCount"] = {{"a", aFC}, {"b", bFC}}; + j["actualRecs"] = {{"a", aActual}, {"b", bActual}}; + j["totalDiffs"] = diffs; + j["identical"] = (diffs == 0); + std::printf("%s\n", j.dump(2).c_str()); + return diffs == 0 ? 0 : 1; + } + auto cmpStr = [](const char* lbl, const std::string& a, const std::string& b) { + std::printf(" %-12s: %-20s %-20s %s\n", lbl, + a.empty() ? "(unset)" : a.c_str(), + b.empty() ? "(unset)" : b.c_str(), + a == b ? "" : "DIFF"); + }; + auto cmpNum = [](const char* lbl, uint32_t a, uint32_t b) { + std::printf(" %-12s: %20u %20u %s\n", lbl, a, b, + a == b ? "" : "DIFF"); + }; + std::printf("Diff: %s vs %s\n", aPath.c_str(), bPath.c_str()); + cmpStr("format", aFmt, bFmt); + cmpStr("source", aSrc, bSrc); + cmpNum("recordCount", aRC, bRC); + cmpNum("fieldCount", aFC, bFC); + cmpNum("actualRecs", aActual, bActual); + if (diffs == 0) { + std::printf(" IDENTICAL\n"); + return 0; + } + return 1; } else if (std::strcmp(argv[i], "--list-wcp") == 0 && i + 1 < argc) { // Like --info-wcp but prints every file path. Useful for spotting // missing or unexpected entries before unpacking.