From cc3e85be5a8057401949305696894e35ae72fd52 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 14:31:35 -0700 Subject: [PATCH] feat(editor): add --migrate-jsondbc to auto-fix common JSON DBC issues MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Designers receive JSON DBCs from various tools (older asset_extract versions, third-party converters) that may omit fields the runtime now expects. --validate-jsondbc tells you what's wrong; this fixes the auto-fixable ones: wowee_editor --migrate-jsondbc db/Spell.json added: format = 'wowee-dbc-json-1.0' added: source = 'Spell.dbc' fixed: recordCount 99 -> 47882 (matches actual) inferred: fieldCount = 234 (from first row) Migrated db/Spell.json -> db/Spell.json fixes applied: 4 Auto-fixes: - Missing 'format' tag → add 'wowee-dbc-json-1.0' - Missing 'source' field → derive from filename stem + '.dbc' - Missing 'fieldCount' → infer from first row - recordCount mismatch → recompute from actual records[] length NOT auto-fixed (data loss risk — surfaced as warnings instead): - Wrong-width rows (silently padding/truncating could mangle field values; the user needs to inspect and decide) In-place by default (writes back to the input path); accepts an optional output path for non-destructive migration. Verified: a JSON missing format/source/fieldCount with mismatched recordCount=99 (actual 2) → migrate applies 4 fixes → --validate-jsondbc reports PASSED on the result. --- tools/editor/main.cpp | 115 +++++++++++++++++++++++++++++++++++++++++- 1 file changed, 114 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 384a09b0..4e231d17 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -421,6 +421,8 @@ static void printUsage(const char* argv0) { std::printf(" Upgrade an older WOM (v1/v2) to WOM3 with a default single-batch entry\n"); std::printf(" --migrate-zone \n"); std::printf(" Run --migrate-wom in-place on every WOM under \n"); + std::printf(" --migrate-jsondbc [out.json]\n"); + std::printf(" Auto-fix a JSON DBC sidecar: add missing format/source, sync recordCount\n"); std::printf(" --list-zones [--json] List discovered custom zones and exit\n"); std::printf(" --zone-stats [--json]\n"); std::printf(" Aggregate counts across every zone in \n"); @@ -667,7 +669,7 @@ int main(int argc, char* argv[]) { "--bake-zone-glb", "--bake-zone-stl", "--bake-zone-obj", "--convert-m2", "--convert-wmo", "--convert-dbc-json", "--convert-json-dbc", "--convert-blp-png", - "--migrate-wom", "--migrate-zone", + "--migrate-wom", "--migrate-zone", "--migrate-jsondbc", }; for (int i = 1; i < argc; i++) { for (const char* opt : kArgRequired) { @@ -9544,6 +9546,117 @@ int main(int argc, char* argv[]) { } return failed == 0 ? 0 : 1; } + if (std::strcmp(argv[i], "--migrate-jsondbc") == 0 && i + 1 < argc) { + // Auto-fix common schema problems in JSON DBC sidecars so they + // pass --validate-jsondbc cleanly. Designed for upgrading + // sidecars produced by older asset_extract versions or from + // third-party tools that omit fields the runtime now expects: + // - missing 'format' tag → add 'wowee-dbc-json-1.0' + // - missing 'source' field → derive from filename + // - missing 'fieldCount' → infer from first row + // - recordCount mismatch → recompute from actual records[] + // Wrong-width rows are not silently fixed (data loss risk); + // they're surfaced as warnings so the user can decide. + std::string path = argv[++i]; + std::string outPath; + if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i]; + if (outPath.empty()) outPath = path; // in-place + std::ifstream in(path); + if (!in) { + std::fprintf(stderr, + "migrate-jsondbc: cannot open %s\n", path.c_str()); + return 1; + } + nlohmann::json doc; + try { in >> doc; } + catch (const std::exception& e) { + std::fprintf(stderr, + "migrate-jsondbc: bad JSON in %s (%s)\n", + path.c_str(), e.what()); + return 1; + } + in.close(); + if (!doc.is_object()) { + std::fprintf(stderr, + "migrate-jsondbc: top-level value is not an object\n"); + return 1; + } + int fixes = 0; + if (!doc.contains("format") || !doc["format"].is_string()) { + doc["format"] = "wowee-dbc-json-1.0"; + fixes++; + std::printf(" added: format = 'wowee-dbc-json-1.0'\n"); + } else if (doc["format"] != "wowee-dbc-json-1.0") { + std::printf(" retained existing format: '%s' (not changed)\n", + doc["format"].get().c_str()); + } + if (!doc.contains("source") || !doc["source"].is_string() || + doc["source"].get().empty()) { + // Derive from input path's stem + .dbc — best-effort + // matching the convention asset_extract uses. + std::string stem = std::filesystem::path(path).stem().string(); + doc["source"] = stem + ".dbc"; + fixes++; + std::printf(" added: source = '%s'\n", + doc["source"].get().c_str()); + } + // recordCount + fieldCount are non-negotiable for re-import. + if (!doc.contains("records") || !doc["records"].is_array()) { + std::fprintf(stderr, + "migrate-jsondbc: 'records' missing or not an array — cannot fix\n"); + return 1; + } + const auto& records = doc["records"]; + uint32_t actualCount = static_cast(records.size()); + uint32_t headerCount = doc.value("recordCount", 0u); + if (headerCount != actualCount) { + doc["recordCount"] = actualCount; + fixes++; + std::printf(" fixed: recordCount %u -> %u (matches actual)\n", + headerCount, actualCount); + } + // Infer fieldCount from first row if missing. + if (!doc.contains("fieldCount") || + !doc["fieldCount"].is_number_integer()) { + if (!records.empty() && records[0].is_array()) { + uint32_t inferred = static_cast(records[0].size()); + doc["fieldCount"] = inferred; + fixes++; + std::printf(" inferred: fieldCount = %u (from first row)\n", + inferred); + } + } + // Surface wrong-width rows as warnings (no auto-fix). + uint32_t fc = doc.value("fieldCount", 0u); + int badRows = 0; + for (size_t r = 0; r < records.size(); ++r) { + if (records[r].is_array() && records[r].size() != fc) { + if (++badRows <= 3) { + std::printf(" WARN: row %zu has %zu cells, expected %u\n", + r, records[r].size(), fc); + } + } + } + if (badRows > 3) { + std::printf(" WARN: ... and %d more wrong-width rows\n", + badRows - 3); + } + std::ofstream out(outPath); + if (!out) { + std::fprintf(stderr, + "migrate-jsondbc: cannot write %s\n", outPath.c_str()); + return 1; + } + out << doc.dump(2) << "\n"; + out.close(); + std::printf("Migrated %s -> %s\n", path.c_str(), outPath.c_str()); + std::printf(" fixes applied: %d\n", fixes); + if (badRows > 0) { + std::printf(" warnings : %d wrong-width rows (NOT auto-fixed)\n", + badRows); + } + return 0; + } } if (dataPath.empty()) {