Last active
July 12, 2026 01:02
-
-
Save kalbasit/03960506c84c7ba4aaf419ee78bdfd88 to your computer and use it in GitHub Desktop.
copy-roms-to-romm.ps1 — robocopy EmuDeck's new-to-RomM platforms (gc/ps2/ps3/ps4/psx/switch/wii/wiiu/xbox360) from C:\Emulation\roms to the TrueNAS games dataset (Z:\emulation\roms); resumable /Z, ps4=zip-only, switch=base-nsp-only
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| <# | |
| copy-roms-to-romm.ps1 | |
| Copies the platforms that are NEW to RomM from your EmuDeck library | |
| (C:\Emulation\roms) onto the TrueNAS "games" dataset you mounted as Z: | |
| (Z:\emulation\roms == RomM's /mnt/tank/emulation/roms). | |
| Already-imported systems (nes / snes / gb) are intentionally NOT copied. | |
| Per-platform rules (as agreed): | |
| - ps4 -> copies ONLY the packaged *.zip (your backup form); skips the | |
| .lnk launcher and the shortcuts\ folder, and does NOT touch the | |
| huge extracted storage\shadps4 tree. | |
| - switch -> copies ONLY the 5 base *.nsp; DLC/updates (storage\ryujinx\ | |
| patchesAndDlc) are left out. | |
| - ps3 -> full recursive copy (JB folder-format games). | |
| - everything else -> full recursive copy. | |
| Resilience: robocopy /Z (restartable mode) resumes large files (ps4 ~30 GB, | |
| wiiu ~13 GB, switch nsp) after a hibernate or dropped link instead of | |
| starting over. Re-running this script is safe and resumable -- it skips | |
| files already present with the same size+timestamp and only finishes what's | |
| missing. | |
| Usage (from PowerShell, in the folder where you want the log written): | |
| powershell -ExecutionPolicy Bypass -File .\copy-roms-to-romm.ps1 | |
| Preview without copying (robocopy /L): | |
| powershell -ExecutionPolicy Bypass -File .\copy-roms-to-romm.ps1 -DryRun | |
| Non-default paths: | |
| powershell -ExecutionPolicy Bypass -File .\copy-roms-to-romm.ps1 -Src "D:\Emulation\roms" -Dst "Z:\emulation\roms" | |
| #> | |
| param( | |
| [string]$Src = "C:\Emulation\roms", | |
| [string]$Dst = "Z:\emulation\roms", | |
| [switch]$DryRun | |
| ) | |
| $ErrorActionPreference = "Stop" | |
| # PS 7.4+ makes native non-zero exit codes throw; robocopy uses 1..7 for SUCCESS, | |
| # so disable that behavior and interpret the exit code ourselves. | |
| $PSNativeCommandUseErrorActionPreference = $false | |
| # Platforms new to RomM. filter = copy only matching files from the TOP of the | |
| # folder (no recurse); absent filter = /E full recursive copy. | |
| $jobs = @( | |
| @{ p = 'gc' }, | |
| @{ p = 'ps2' }, | |
| @{ p = 'ps3' }, | |
| @{ p = 'ps4'; filter = '*.zip'; recurse = $true }, # zip is nested -> recurse to find it | |
| @{ p = 'psx' }, | |
| @{ p = 'switch'; filter = '*.nsp' }, | |
| @{ p = 'wii' }, | |
| @{ p = 'wiiu' }, | |
| @{ p = 'xbox360' } | |
| ) | |
| if (-not (Test-Path $Src)) { throw "Source not found: $Src" } | |
| $dstDrive = Split-Path $Dst -Qualifier # e.g. "Z:" | |
| if (-not (Test-Path "$dstDrive\")) { | |
| throw "Destination drive $dstDrive is not available. Is the TrueNAS 'games' dataset mounted as $dstDrive ?" | |
| } | |
| $stamp = Get-Date -Format "yyyyMMdd-HHmmss" | |
| $logFile = Join-Path (Get-Location) "romm-copy-$stamp.log" | |
| Write-Host "=== Copy EmuDeck ROMs -> RomM library ===" | |
| Write-Host " Source : $Src" | |
| Write-Host " Dest : $Dst" | |
| Write-Host " Log : $logFile" | |
| if ($DryRun) { Write-Host " MODE : DRY RUN (robocopy /L -- lists only, copies nothing)" -ForegroundColor Yellow } | |
| # /Z restartable /R:3 /W:5 bounded retries /NP quiet % /TEE console+log /LOG+ append | |
| # /XF ... skip ES-DE scraper cruft (never ROMs); per-job /XD skips the media\ artwork folder. | |
| $common = @('/Z', '/R:3', '/W:5', '/NP', '/TEE', "/LOG+:$logFile", | |
| '/XF', 'metadata.txt', 'systeminfo.txt', 'gamelist.xml', 'desktop.ini') | |
| if ($DryRun) { $common += '/L' } | |
| $results = @() | |
| foreach ($j in $jobs) { | |
| $p = $j.p | |
| $sdir = Join-Path $Src $p | |
| $ddir = Join-Path $Dst $p | |
| if (-not (Test-Path $sdir)) { | |
| Write-Host "" | |
| Write-Host "-- skip $p (no source folder: $sdir)" -ForegroundColor DarkYellow | |
| $results += [pscustomobject]@{ Platform = $p; Code = -1; Status = 'skipped (missing source)' } | |
| continue | |
| } | |
| $roboArgs = @($sdir, $ddir) | |
| if ($j.filter) { | |
| $roboArgs += $j.filter | |
| if ($j.recurse) { $roboArgs += '/S' } # recurse to find a nested package (ps4 *.zip) | |
| } else { | |
| $roboArgs += '/E' | |
| } | |
| $roboArgs += $common | |
| $roboArgs += @('/XD', (Join-Path $sdir 'media')) # skip the ES-DE artwork folder at platform root | |
| $mode = if ($j.filter) { "[$($j.filter) only]" } else { "[full recursive]" } | |
| Write-Host "" | |
| Write-Host ("-- {0} {1}" -f $p, $mode) -ForegroundColor Cyan | |
| Write-Host (" {0} -> {1}" -f $sdir, $ddir) | |
| & robocopy @roboArgs | |
| $code = $LASTEXITCODE | |
| $status = | |
| if ($code -ge 8) { "FAILED (code $code)" } | |
| elseif ($code -eq 0) { "already in sync" } | |
| else { "copied ok (code $code)" } | |
| $results += [pscustomobject]@{ Platform = $p; Code = $code; Status = $status } | |
| } | |
| Write-Host "" | |
| Write-Host "==================== SUMMARY ====================" | |
| $results | Format-Table -AutoSize | Out-String | Write-Host | |
| $failed = @($results | Where-Object { $_.Code -ge 8 }) | |
| if ($failed.Count -gt 0) { | |
| Write-Host ("{0} platform(s) had errors -- see the log. Re-run this script to resume." -f $failed.Count) -ForegroundColor Red | |
| } else { | |
| Write-Host "All done. Re-running is safe (verifies + copies only anything new)." -ForegroundColor Green | |
| } | |
| Write-Host "Log: $logFile" | |
| Write-Host "" | |
| Write-Host "Next (Claude's side): RomM slug remaps gc->ngc and psx->ps, then a library scan." |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment