From 61fc3847c315dc54ac4878ef14e5c7b2a44d7529 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 7 May 2026 00:42:55 -0700 Subject: [PATCH] feat(editor): add --export-data-tree-md migration progress markdown report MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Generates MIGRATION.md with: status badge ("100% migrated", "Mostly migrated", "Partially migrated", "Migration pending"), summary counts, per-pair table with shares, and recommended next steps as copy-pasteable wowee_editor invocations. Drops cleanly into PR descriptions, CI artifacts, or GitHub Pages status pages. Verified: 50%-migrated test tree → "Partially migrated" badge, correct per-pair shares (.m2→.wom 100%, .blp→.png 0%), recommended steps point at the actual srcDir. Brings command count to 193. --- tools/editor/main.cpp | 131 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 131 insertions(+) diff --git a/tools/editor/main.cpp b/tools/editor/main.cpp index 183feac6..02fb198f 100644 --- a/tools/editor/main.cpp +++ b/tools/editor/main.cpp @@ -522,6 +522,8 @@ static void printUsage(const char* argv0) { std::printf(" Time each step of --migrate-data-tree (m2/wmo/blp/dbc) and report wall-clock per step\n"); std::printf(" --list-data-tree-largest [N]\n"); std::printf(" Top-N largest proprietary files (.m2/.wmo/.blp/.dbc) for migration prioritization\n"); + std::printf(" --export-data-tree-md [out.md]\n"); + std::printf(" Markdown migration-progress report (per-pair table, share %%, recommended next steps)\n"); std::printf(" --convert-dbc-json [out.json]\n"); std::printf(" Convert one DBC file to wowee JSON sidecar format\n"); std::printf(" --convert-json-dbc [out.dbc]\n"); @@ -905,6 +907,7 @@ int main(int argc, char* argv[]) { "--validate-project-open-only", "--audit-project", "--bench-validate-project", "--bench-bake-project", "--bench-migrate-data-tree", "--list-data-tree-largest", + "--export-data-tree-md", "--validate-glb", "--info-glb", "--info-glb-tree", "--info-glb-bytes", "--validate-jsondbc", "--check-glb-bounds", "--validate-stl", "--validate-png", "--validate-blp", @@ -13734,6 +13737,134 @@ int main(int argc, char* argv[]) { e.path.c_str()); } return 0; + } else if (std::strcmp(argv[i], "--export-data-tree-md") == 0 && i + 1 < argc) { + // Markdown migration-progress report. Drops cleanly into + // PR descriptions, CI artifacts, or status pages on + // GitHub Pages. Same numbers as --info-data-tree but + // formatted as a Markdown table with a status badge, + // bytes summary, and recommended next steps so a reader + // can act on the report without consulting the CLI help. + std::string srcDir = argv[++i]; + std::string outPath; + if (i + 1 < argc && argv[i + 1][0] != '-') outPath = argv[++i]; + namespace fs = std::filesystem; + if (!fs::exists(srcDir) || !fs::is_directory(srcDir)) { + std::fprintf(stderr, + "export-data-tree-md: %s is not a directory\n", + srcDir.c_str()); + return 1; + } + if (outPath.empty()) outPath = srcDir + "/MIGRATION.md"; + static const std::vector> + kPairs = { + {".m2", ".wom"}, + {".wmo", ".wob"}, + {".blp", ".png"}, + {".dbc", ".json"}, + }; + // Same scan as --info-data-tree. + std::map>> + byExt; + std::map bytesByExt; + std::error_code ec; + for (const auto& e : fs::recursive_directory_iterator(srcDir, ec)) { + if (!e.is_regular_file()) continue; + std::string ext = e.path().extension().string(); + std::transform(ext.begin(), ext.end(), ext.begin(), + [](unsigned char c) { return std::tolower(c); }); + byExt[ext].insert({e.path().parent_path().string(), + e.path().stem().string()}); + uint64_t sz = e.file_size(ec); + if (!ec) bytesByExt[ext] += sz; + } + struct Row { + std::string prop, open; + int propCount, sidecarCount, orphanOpenCount; + uint64_t propBytes; + double share; + }; + std::vector rows; + int totalProp = 0, totalSidecar = 0, totalOrphan = 0; + uint64_t totalPropBytes = 0; + for (const auto& [propExt, openExt] : kPairs) { + Row r{propExt, openExt, 0, 0, 0, 0, 0.0}; + const auto& propSet = byExt[propExt]; + const auto& openSet = byExt[openExt]; + r.propCount = static_cast(propSet.size()); + for (const auto& key : openSet) { + if (propSet.count(key)) r.sidecarCount++; + else r.orphanOpenCount++; + } + r.propBytes = bytesByExt[propExt]; + r.share = r.propCount > 0 + ? 100.0 * r.sidecarCount / r.propCount + : 100.0; + totalProp += r.propCount; + totalSidecar += r.sidecarCount; + totalOrphan += r.orphanOpenCount; + totalPropBytes += r.propBytes; + rows.push_back(r); + } + double overallShare = totalProp > 0 + ? 100.0 * totalSidecar / totalProp + : 100.0; + const char* badge = + overallShare >= 100.0 ? "**100% migrated**" : + overallShare >= 75.0 ? "**Mostly migrated**" : + overallShare >= 25.0 ? "*Partially migrated*" : + "*Migration pending*"; + std::ofstream out(outPath); + if (!out) { + std::fprintf(stderr, + "export-data-tree-md: cannot write %s\n", outPath.c_str()); + return 1; + } + out << "# Data Tree Migration Report\n\n"; + out << "Source: `" << srcDir << "`\n\n"; + out << "Status: " << badge << " (" << std::fixed; + out.precision(1); + out << overallShare << "% sidecar coverage)\n\n"; + out << "## Summary\n\n"; + out << "- Proprietary files: **" << totalProp << "** (" + << std::fixed; + out.precision(2); + out << (totalPropBytes / (1024.0 * 1024.0)) << " MB)\n"; + out << "- Open sidecars present: **" << totalSidecar << "**\n"; + out << "- Orphan open files (no proprietary source): **" + << totalOrphan << "**\n\n"; + out << "## Per-format pairs\n\n"; + out << "| Pair | Proprietary | Sidecars | Orphan open | Prop bytes | Share |\n"; + out << "|------|------------:|---------:|------------:|-----------:|------:|\n"; + for (const auto& r : rows) { + out << "| " << r.prop << " → " << r.open << " | " + << r.propCount << " | " + << r.sidecarCount << " | " + << r.orphanOpenCount << " | " + << r.propBytes << " | " + << std::fixed; + out.precision(1); + out << r.share << "% |\n"; + } + out << "\n## Recommended next steps\n\n"; + if (overallShare < 100.0) { + out << "1. Run `wowee_editor --migrate-data-tree " << srcDir + << "` to fill in the missing sidecars.\n"; + out << "2. Run `wowee_editor --audit-data-tree " << srcDir + << "` to confirm 100% coverage.\n"; + out << "3. Run `wowee_editor --strip-data-tree " << srcDir + << "` to delete the proprietary originals.\n"; + } else { + out << "All proprietary files are migrated. Run " + << "`wowee_editor --strip-data-tree " << srcDir + << "` to delete the originals and ship the open-only tree.\n"; + } + out.close(); + std::printf("Wrote %s\n", outPath.c_str()); + std::printf(" status : %s\n", badge); + std::printf(" share : %.1f%%\n", overallShare); + std::printf(" proprietary : %d files, %.2f MB\n", + totalProp, totalPropBytes / (1024.0 * 1024.0)); + return 0; } else if (std::strcmp(argv[i], "--info-data-tree") == 0 && i + 1 < argc) { // Non-destructive companion to --migrate-data-tree. Walks // recursively, counts files per format pair