Place RPG-Maker/Essentials hacks into RomM's windows library with dry-run migration, Playnite Game.exe launcher, and Ludusavi save-path manifest.
68 lines
2.8 KiB
Python
68 lines
2.8 KiB
Python
#!/usr/bin/env python3
|
|
"""Generate a Ludusavi secondary manifest for the PC hacks served by RomM.
|
|
|
|
Pokémon Essentials (v18+) writes saves to:
|
|
%USERPROFILE%/Saved Games/<Game.ini Title>/Game.rxdata
|
|
The exact subfolder is the `Title=` field of each game's Game.ini, so we read it
|
|
straight out of the placed archive instead of guessing. Older Essentials (<=v17)
|
|
saved into the game folder itself — covered by the commented `<base>` fallback.
|
|
|
|
Point Ludusavi at the emitted YAML as a secondary/custom manifest, then pair its
|
|
backup target with Syncthing to replicate saves across Windows / Steam Deck.
|
|
|
|
Run on valhalla after romhack-pc-migrate.py --apply:
|
|
python3 gen-ludusavi-manifest.py > ludusavi/pokemon-pc-hacks.yaml
|
|
"""
|
|
import os, sys, zipfile, configparser, io
|
|
|
|
WIN = "/storage1/Emulation/roms/windows"
|
|
|
|
|
|
def sq(s):
|
|
"""YAML single-quoted scalar: an embedded apostrophe must be doubled."""
|
|
return "'" + str(s).replace("'", "''") + "'"
|
|
|
|
|
|
def game_title(zip_path):
|
|
"""Pull Title= from the first */Game.ini inside the archive, if present."""
|
|
try:
|
|
with zipfile.ZipFile(zip_path) as z:
|
|
ini = next((n for n in z.namelist() if n.lower().endswith("game.ini")), None)
|
|
if not ini:
|
|
return None
|
|
raw = z.read(ini).decode("utf-8", "ignore")
|
|
cp = configparser.ConfigParser(strict=False, interpolation=None)
|
|
cp.read_string(raw if raw.lstrip().startswith("[") else "[Game]\n" + raw)
|
|
for sect in cp.sections():
|
|
if cp.has_option(sect, "Title"):
|
|
return cp.get(sect, "Title").strip()
|
|
except Exception as e:
|
|
print(f"# WARN {os.path.basename(zip_path)}: {e}", file=sys.stderr)
|
|
return None
|
|
|
|
|
|
def main():
|
|
print("# Ludusavi secondary manifest — Pokémon PC hacks (generated)")
|
|
print("# Add via Ludusavi > Settings > Manifests, or merge into a custom manifest.")
|
|
print("# Save path = ~/Saved Games/<Game.ini Title> (Essentials v18+).")
|
|
print("# Verify each on first save; for Essentials <=v17 uncomment the <base> line.\n")
|
|
zips = sorted(f for f in os.listdir(WIN) if f.lower().endswith(".zip"))
|
|
for f in zips:
|
|
name = os.path.splitext(f)[0] # "Pokemon - <Hack> (Hack)"
|
|
title = game_title(os.path.join(WIN, f))
|
|
print(f"{sq(name)}:")
|
|
print(" files:")
|
|
if title:
|
|
print(f" {sq('<home>/Saved Games/' + title)}:")
|
|
print(" tags: [save]")
|
|
else:
|
|
print(f" # TODO no Game.ini Title found — set save path manually")
|
|
print(f" {sq('<home>/Saved Games/' + name)}:")
|
|
print(" tags: [save]")
|
|
print(" # '<base>': # older Essentials saved in the game folder")
|
|
print(" # tags: [save]")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|