From 14492abc99390607acd7eda4cf68fb703d484202 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Wed, 6 May 2026 21:44:47 -0700 Subject: [PATCH] feat(editor): add --repair-project for project-wide manifest self-healing MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Wraps --repair-zone across every zone in . Spawns the binary per-zone so each zone's full repair report streams through, with section markers between, then aggregates a final tally. Honors --dry-run. Calls fflush(stdout) before each child spawn so the parent's section header lands ahead of the child's stdout (separate buffers per process). Verified: clean project → 0 fixes / exit 0; project with an orphan tile file → "would add tile (5, 5) to manifest" / exit 0 (dry-run). Brings command count to 177. --- 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 bb02ecd6..68e1f509 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -592,6 +592,8 @@ static void printUsage(const char* argv0) { std::printf(" Generate a Makefile that rebuilds every derived output for a zone\n"); std::printf(" --gen-project-makefile [out.mk]\n"); std::printf(" Generate a top-level Makefile that delegates to each zone's per-zone Makefile\n"); + std::printf(" --repair-project [--dry-run]\n"); + std::printf(" Run --repair-zone across every zone (manifest drift fixes, per-zone summary)\n"); std::printf(" --repair-zone [--dry-run]\n"); std::printf(" Auto-fix manifest/disk drift (missing tiles in manifest, hasCreatures flag)\n"); std::printf(" --build-woc Generate a WOC collision mesh from WHM/WOT and exit\n"); @@ -895,7 +897,8 @@ int main(int argc, char* argv[]) { "--remove-creature", "--remove-object", "--remove-quest", "--copy-zone", "--rename-zone", "--remove-zone", "--clear-zone-content", "--strip-zone", "--strip-project", - "--repair-zone", "--gen-makefile", "--gen-project-makefile", + "--repair-zone", "--repair-project", + "--gen-makefile", "--gen-project-makefile", "--build-woc", "--regen-collision", "--fix-zone", "--export-png", "--export-obj", "--import-obj", "--export-wob-obj", "--import-wob-obj", @@ -13015,6 +13018,54 @@ int main(int argc, char* argv[]) { std::printf(" re-run without --dry-run to apply\n"); } return 0; + } else if (std::strcmp(argv[i], "--repair-project") == 0 && i + 1 < argc) { + // Project-wide wrapper around --repair-zone. Spawns the + // binary per-zone so each zone's full repair report + // streams through, then aggregates a final tally. Honors + // --dry-run for safe previews. + std::string projectDir = argv[++i]; + bool dryRun = false; + if (i + 1 < argc && std::strcmp(argv[i + 1], "--dry-run") == 0) { + dryRun = true; i++; + } + namespace fs = std::filesystem; + if (!fs::exists(projectDir) || !fs::is_directory(projectDir)) { + std::fprintf(stderr, + "repair-project: %s is not a directory\n", + projectDir.c_str()); + return 1; + } + std::vector zones; + for (const auto& entry : fs::directory_iterator(projectDir)) { + if (!entry.is_directory()) continue; + if (!fs::exists(entry.path() / "zone.json")) continue; + zones.push_back(entry.path().string()); + } + std::sort(zones.begin(), zones.end()); + std::string self = argv[0]; + int totalFailed = 0; + std::printf("repair-project: %s%s\n", + projectDir.c_str(), dryRun ? " (dry-run)" : ""); + std::printf(" zones : %zu\n", zones.size()); + for (const auto& zoneDir : zones) { + std::printf("\n--- %s ---\n", + fs::path(zoneDir).filename().string().c_str()); + // Flush so the section marker lands before the spawned + // child's stdout — std::system inherits FDs but each + // process has its own buffer. + std::fflush(stdout); + std::string cmd = "\"" + self + "\" --repair-zone \"" + + zoneDir + "\"" + (dryRun ? " --dry-run" : ""); + int rc = std::system(cmd.c_str()); + if (rc != 0) totalFailed++; + } + std::printf("\n--- summary ---\n"); + std::printf(" zones processed : %zu\n", zones.size()); + std::printf(" failures : %d\n", totalFailed); + if (dryRun) { + std::printf(" re-run without --dry-run to apply changes\n"); + } + return totalFailed == 0 ? 0 : 1; } else if (std::strcmp(argv[i], "--gen-makefile") == 0 && i + 1 < argc) { // Generate a Makefile that rebuilds every derived output for // a zone. With this in place, designers can `make` to refresh