#!/usr/bin/env pwsh # scripts/igir-report.ps1 # # Run the igir ROM collection manager in REPORT mode (read-only) against the # EmuDeck tree at /storage1/Emulation/roms on valhalla, matching ROMs by # checksum against the full No-Intro + Redump DAT sets. Produces a CSV per run # under /storage1/igir/reports listing FOUND / MISSING / UNUSED per system. # # Nothing is moved, renamed, or deleted — the ROM tree is mounted read-only. # # ./scripts/igir-report.ps1 -Platforms gba,snes,n64,psx # scoped run (fast) # ./scripts/igir-report.ps1 -All # whole library (SLOW: hashes everything) # ./scripts/igir-report.ps1 -Platforms snes -UpdateDats # refresh the cached DATs first # # Gotchas baked in (all learned the hard way — see memory igir-roms): # * Uses the Debian `node:lts` image, NOT node:lts-alpine (igir silently # dies on musl). # * DATs come from libretro-database `metadat/no-intro` + `metadat/redump` # (full sets with checksums), NOT the tiny curated top-level `dat/` folder. # * SNES `.smc` copier headers (size%1024==512) are NOT auto-detected by igir # and won't match headerless No-Intro until physically stripped. The `snes` # folder was stripped 2026-06-06; if you add more headered SNES ROMs, strip # them first (see scripts/igir-strip-snes-headers.sh) or they'll show UNUSED. [CmdletBinding()] param( [string[]]$Platforms, [switch]$All, [switch]$UpdateDats, [string]$Hostname = 'valhalla' ) $ErrorActionPreference = 'Stop' $PSNativeCommandUseErrorActionPreference = $true $server = "ginnoir@$Hostname" if (-not $All -and (-not $Platforms -or $Platforms.Count -eq 0)) { Write-Error "Specify -Platforms (e.g. -Platforms gba,snes) or -All for the whole library." exit 1 } # Build the igir -i input args. -All scans the entire roms tree (heavy); # otherwise one -i per named platform folder. if ($All) { $inputArgs = '-i /input' } else { $inputArgs = ($Platforms | ForEach-Object { "-i /input/$_" }) -join ' ' } $doPull = if ($UpdateDats) { '1' } else { '0' } # Remote logic runs on valhalla. Single-quoted here-string so PowerShell leaves # $vars for bash; INPUT_ARGS / DOPULL are injected below. $remote = @' set -uo pipefail DATS=/storage1/igir/dats/libretro-database REPORTS=/storage1/igir/reports mkdir -p "$REPORTS" /storage1/igir/dats # Sparse, blobless clone of just the No-Intro + Redump DAT dirs (cached & reused). if [ ! -d "$DATS/.git" ]; then echo ">> Fetching libretro-database DATs (one-time sparse clone)..." docker run --rm -v /storage1/igir:/w -w /w node:lts sh -c ' git clone --filter=blob:none --sparse --depth 1 \ https://github.com/libretro/libretro-database dats/libretro-database && cd dats/libretro-database && git sparse-checkout set metadat/no-intro metadat/redump' elif [ "__DOPULL__" = "1" ]; then echo ">> Updating cached DATs..." docker run --rm -v /storage1/igir:/w -w /w/dats/libretro-database node:lts git pull -q fi NI=$(find "$DATS/metadat/no-intro" -name '*.dat' | wc -l) RD=$(find "$DATS/metadat/redump" -name '*.dat' | wc -l) echo ">> DATs available: $NI No-Intro, $RD Redump" STAMP=$(date +%Y-%m-%dT%H-%M-%S) OUT="igir-report-$STAMP.csv" echo ">> Running igir report -> $REPORTS/$OUT" docker run --rm \ -v /storage1/Emulation/roms:/input:ro \ -v "$DATS":/dats:ro \ -v "$REPORTS":/output \ node:lts npx -y igir@latest report \ -d /dats/metadat/no-intro -d /dats/metadat/redump \ __INPUT_ARGS__ \ --report-output "/output/$OUT" 2>&1 | grep -vE 'npm notice|npm warn' || true echo ">> Done. CSV: /storage1/igir/reports/$OUT" '@ $remote = $remote.Replace('__INPUT_ARGS__', $inputArgs).Replace('__DOPULL__', $doPull) # Strip CR so the remote bash never sees stray \r (this file may be CRLF on disk). $remote = $remote.Replace("`r", '') Write-Host "igir report on $server :: $(if ($All) {'ALL platforms'} else {$Platforms -join ', '})" ssh $server $remote