Skip to content

Instantly share code, notes, and snippets.

@kalbasit
Last active July 12, 2026 01:02
Show Gist options
  • Select an option

  • Save kalbasit/03960506c84c7ba4aaf419ee78bdfd88 to your computer and use it in GitHub Desktop.

Select an option

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
<#
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