feat(extract): incremental --upgrade-extract skips up-to-date sidecars

Compares the source file's mtime against the sidecar's; if the
sidecar is newer, the conversion is skipped and counted into
stats.skipped. Re-running --upgrade-extract on a fully-converted
tree is now nearly free (just an mtime check per file).

  asset_extract --upgrade-extract Data/expansions/wotlk
  Walking ... (first run)
    JSON (DBC→JSON)   : 240 ok
  asset_extract --upgrade-extract Data/expansions/wotlk
  Walking ... (second run, all sidecars up to date)
    up-to-date (skip) : 240
    JSON (DBC→JSON)   : 0 ok

emitOpenFormats() takes a new optional 'incremental' flag (default
false to preserve the asset_extract main-loop's overwrite behavior
since fresh extraction always wants new sidecars).

Verified end-to-end with a hand-built DBC: first run converts,
second run reports 'up-to-date (skip): 1'.
This commit is contained in:
Kelsi 2026-05-06 11:00:20 -07:00
parent 463a8cd751
commit 397034a750
3 changed files with 49 additions and 8 deletions

View file

@ -125,13 +125,18 @@ int main(int argc, char** argv) {
wowee::tools::OpenFormatStats stats;
// Pass 0 to auto-detect threads (or honor user --threads override).
unsigned int t = opts.threads > 0 ? static_cast<unsigned int>(opts.threads) : 0;
// upgrade-extract is always incremental — skip files whose sidecar
// is already up to date so re-runs are cheap.
wowee::tools::emitOpenFormats(upgradeDir,
opts.emitPng, opts.emitJsonDbc,
opts.emitWom, opts.emitWob,
opts.emitTerrain, stats, t);
opts.emitTerrain, stats, t,
/*incremental=*/true);
auto secs = std::chrono::duration_cast<std::chrono::milliseconds>(
std::chrono::steady_clock::now() - t0).count() / 1000.0;
std::cout << " elapsed : " << secs << " s\n";
if (stats.skipped > 0)
std::cout << " up-to-date (skip) : " << stats.skipped << "\n";
std::cout << " PNG (BLP→PNG) : " << stats.pngOk << " ok"
<< (stats.pngFail ? ", " + std::to_string(stats.pngFail) + " failed" : "") << "\n";
std::cout << " JSON (DBC→JSON) : " << stats.jsonDbcOk << " ok"