Switch the library naming convention from "Pokemon - <Name> (Hack)" to "Pokemon - <Name> [Hack][version][tags]", and place hacks in the platform root (roms/<plat>/) instead of a Hacks/ subfolder. Applied across the live valhalla library (272 hack/fan-game files) via scripts/standardize-names.py. - romhack-import.py / romhack-fetch.py: emit [Hack], write to roms/<plat>/ - romhack-pc-migrate.py: emit [Hack] - build-romhack-vault.py (archived): update documented library_path - standardize-names.py: new one-shot, self-fetching standardizer (idempotent; pipes \n-only bytes over SSH to avoid Windows CRLF corrupting filenames) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
94 lines
3.3 KiB
Python
94 lines
3.3 KiB
Python
#!/usr/bin/env python3
|
|
"""
|
|
Standardize valhalla Hack-library filenames to:
|
|
|
|
Pokemon - <Name> [Hack][<version>][<functional tags>].<ext>
|
|
|
|
Rule-based + self-fetching: queries valhalla for every file still tagged
|
|
"(Hack)" anywhere under the roms tree and rewrites it.
|
|
|
|
- mechanical: " (Hack)" -> " [Hack]" (preserves other (...) like (FRLG+))
|
|
- OVERRIDES: platform-port fan-games whose stem carries "-<ver>-<os>"
|
|
get the version pulled into [ver] and the OS kept as a tag.
|
|
- SKIP: non-game patcher utilities are left untouched.
|
|
|
|
Dry-run prints OLD -> NEW; --apply runs `mv -n` over SSH.
|
|
Idempotent: already-[Hack] files don't match the (Hack) query, so re-runs
|
|
are no-ops.
|
|
"""
|
|
import subprocess, sys, shlex, os
|
|
|
|
APPLY = "--apply" in sys.argv
|
|
HOST = "valhalla"
|
|
ROOT = "/storage1/Emulation/roms"
|
|
|
|
# Non-game utilities — leave alone.
|
|
SKIP = {
|
|
"Pokemon - DeltaPatcherLite (Hack) [Installer].zip",
|
|
"Pokemon - Patcher (Hack) [Installer].zip",
|
|
}
|
|
|
|
# Stem-encoded version/OS ports -> canonical [Hack][ver][OS].
|
|
OVERRIDES = {
|
|
"Pokemon - Reborn-19.5.0-linux (Hack) [Linux].zip": "Pokemon - Reborn [Hack][19.5.0][Linux].zip",
|
|
"Pokemon - Rejuvenation-13.5.0-linux (Hack) [Linux].zip": "Pokemon - Rejuvenation [Hack][13.5.0][Linux].zip",
|
|
"Pokemon - Reborn-19.5.0-macos (Hack) [macOS].zip": "Pokemon - Reborn [Hack][19.5.0][macOS].zip",
|
|
"Pokemon - Rejuvenation-13.5.0-macos (Hack) [macOS].zip": "Pokemon - Rejuvenation [Hack][13.5.0][macOS].zip",
|
|
}
|
|
|
|
|
|
def new_name(base):
|
|
if base in SKIP:
|
|
return None
|
|
if base in OVERRIDES:
|
|
return OVERRIDES[base]
|
|
if " (Hack)" in base:
|
|
return base.replace(" (Hack)", " [Hack]")
|
|
return None
|
|
|
|
|
|
def fetch():
|
|
cmd = f"find {shlex.quote(ROOT)} -type f -name '*(Hack)*'"
|
|
out = subprocess.run(["ssh", HOST, cmd], capture_output=True, text=True, check=True).stdout
|
|
return sorted(p for p in out.splitlines() if p.strip())
|
|
|
|
|
|
def main():
|
|
paths = fetch()
|
|
cmds, special, mech, skipped = [], [], 0, []
|
|
for full in paths:
|
|
d, base = os.path.dirname(full), os.path.basename(full)
|
|
nn = new_name(base)
|
|
if nn is None:
|
|
skipped.append(base)
|
|
continue
|
|
cmds.append(f"mv -n -- {shlex.quote(full)} {shlex.quote(d + '/' + nn)}")
|
|
if base in OVERRIDES:
|
|
special.append((base, nn))
|
|
else:
|
|
mech += 1
|
|
|
|
print(f"Found {len(paths)} files still tagged (Hack).\n")
|
|
print(f"Mechanical (Hack)->[Hack] swaps : {mech}")
|
|
print(f"Special version/OS overrides : {len(special)}")
|
|
for o, n in special:
|
|
print(f" {o}\n -> {n}")
|
|
print(f"Skipped (non-game utilities) : {len(skipped)}")
|
|
for s in skipped:
|
|
print(f" {s}")
|
|
|
|
if not APPLY:
|
|
print("\n(dry-run — pass --apply to execute over SSH)")
|
|
return
|
|
# NB: pipe bytes with explicit \n line endings. On Windows, text=True wraps
|
|
# stdin in a TextIOWrapper that rewrites \n -> \r\n, which bash would append
|
|
# as a stray CR onto every destination filename. Encoding ourselves avoids it.
|
|
script = "set -e\n" + "\n".join(cmds) + "\n"
|
|
r = subprocess.run(["ssh", HOST, "bash -s"], input=script.encode("utf-8"))
|
|
print(f"\napplied {len(cmds)} renames (exit {r.returncode})")
|
|
sys.exit(r.returncode)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|