From 6d79963f24f1ab4fc0e9ab374ff7d0924dcf6cc7 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 13:44:16 -0700 Subject: [PATCH] feat(editor): add --migrate-zone for batch WOM upgrade across a zone Companion to --migrate-wom (single-file upgrade). Walks a zone dir recursively and upgrades every legacy WOM (v1/v2 with no batches[]) to WOM3 in-place: wowee_editor --migrate-zone custom_zones/MyZone upgraded: custom_zones/MyZone/models/v1_0.wom upgraded: custom_zones/MyZone/models/v1_1.wom migrate-zone: custom_zones/MyZone scanned : 3 WOM file(s) upgraded : 2 (added single-batch entry) already v3: 1 (no change needed) Idempotent: already-migrated files become no-ops, so it's safe to run inside --for-each-zone over a whole project. Useful when the editor adds a new WOM3-only feature and legacy zones need a one-shot upgrade pass. Returns exit 1 if any file fails to write (separate from 'no upgrade needed'), so CI can gate on it. Verified: seeded a dir with 2 v1 WOMs + 1 v3 WOM. First run upgraded 2, reported 1 as already-v3. Re-run reported all 3 as already-v3 (no double-batching). --- tools/editor/main.cpp | 53 ++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 15f91c18..a8c6fdd8 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -416,6 +416,8 @@ static void printUsage(const char* argv0) { std::printf(" Convert one BLP texture to PNG sidecar\n"); std::printf(" --migrate-wom [out-base]\n"); 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(" --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"); @@ -623,7 +625,7 @@ int main(int argc, char* argv[]) { "--export-glb", "--export-wob-glb", "--export-whm-glb", "--convert-m2", "--convert-wmo", "--convert-dbc-json", "--convert-json-dbc", "--convert-blp-png", - "--migrate-wom", + "--migrate-wom", "--migrate-zone", }; for (int i = 1; i < argc; i++) { for (const char* opt : kArgRequired) { @@ -7460,6 +7462,55 @@ int main(int argc, char* argv[]) { } return 0; } + if (std::strcmp(argv[i], "--migrate-zone") == 0 && i + 1 < argc) { + // Batch-runs --migrate-wom in-place on every .wom under + // a zone directory. Idempotent (already-migrated files + // become no-ops). Useful when wowee_editor adds a new + // WOM3-only feature and you want to upgrade legacy zones + // in one shot. + std::string zoneDir = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(zoneDir) || !fs::is_directory(zoneDir)) { + std::fprintf(stderr, + "migrate-zone: %s is not a directory\n", zoneDir.c_str()); + return 1; + } + int scanned = 0, upgraded = 0, alreadyV3 = 0, failed = 0; + std::error_code ec; + for (const auto& e : fs::recursive_directory_iterator(zoneDir, ec)) { + if (!e.is_regular_file()) continue; + std::string ext = e.path().extension().string(); + if (ext != ".wom") continue; + scanned++; + std::string base = e.path().string(); + if (base.size() >= 4) base = base.substr(0, base.size() - 4); + auto wom = wowee::pipeline::WoweeModelLoader::load(base); + if (!wom.isValid()) { failed++; continue; } + if (!wom.batches.empty()) { alreadyV3++; continue; } + wowee::pipeline::WoweeModel::Batch b; + b.indexStart = 0; + b.indexCount = static_cast(wom.indices.size()); + b.textureIndex = 0; + b.blendMode = 0; + b.flags = 0; + wom.batches.push_back(b); + if (wowee::pipeline::WoweeModelLoader::save(wom, base)) { + upgraded++; + std::printf(" upgraded: %s.wom\n", base.c_str()); + } else { + failed++; + std::fprintf(stderr, " FAILED: %s.wom\n", base.c_str()); + } + } + std::printf("\nmigrate-zone: %s\n", zoneDir.c_str()); + std::printf(" scanned : %d WOM file(s)\n", scanned); + std::printf(" upgraded : %d (added single-batch entry)\n", upgraded); + std::printf(" already v3: %d (no change needed)\n", alreadyV3); + if (failed > 0) { + std::printf(" FAILED : %d (see stderr)\n", failed); + } + return failed == 0 ? 0 : 1; + } } if (dataPath.empty()) {