#!/usr/bin/env python3 """Create vault notes for list-import hacks missing from catalog.""" import json, re, textwrap, unicodedata from datetime import date from pathlib import Path VAULT = Path(r"C:\Users\MattC\Documents\Obsidian Vault\Pokémon ROM Hacks\Hacks") CATALOG = Path(__file__).resolve().parents[1] / "wiki" / "catalog.json" NEW = [ ("Alchemist", "GBA", "FireRed", "Complete", "https://www.pokecommunity.com/threads/pokémon-alchemist.378280/"), ("Concealed", "PC", None, "Complete", "https://www.pokecommunity.com/threads/concealed.527325/"), ("Consonancia", "PC", None, "Complete", "https://www.pokecommunity.com/threads/pokémon-consonancia-full-game.537329/"), ("Crown", "PC", None, "Ongoing", "https://www.pokecommunity.com/threads/pokémon-crown.463821/"), ("Eon Guardians", "GBA", "Emerald", "Complete", "https://www.pokecommunity.com/threads/pokémon-eon-guardians.527913/"), ("Fire Ash", "GBA", "FireRed", "Complete", "https://www.pokecommunity.com/threads/pokémon-fire-ash.399096/"), ("Floral Tempus", "GBA", "Emerald", "Complete", "https://www.pokecommunity.com/threads/pokémon-floral-tempus-ex-1-9-4-released-12-gym-badges-elite-4-champion.409175/"), ("Hero Legacy", "PC", None, "Complete", "https://www.pokecommunity.com/threads/pokémon-hero-legacy-eng-de-now-with-new-game-mode.538518/"), ("HGSS Sevii", "NDS", "HeartGold", "Complete", "https://www.pokecommunity.com/threads/pokémon-hgss-sevii-islands-hoenn-postgame.434636/"), ("Itinerant", "GBA", "FireRed", "Ongoing", "https://www.pokecommunity.com/threads/pokemon-itinerant.528218/"), ("Jam Festival", "PC", None, "Complete", "https://www.pokecommunity.com/threads/pkmn-jam-festival.536702/"), ("Legends of the Arena", "GBA", "Emerald", "Complete", "https://www.pokecommunity.com/threads/pokémon-legends-of-the-arena.298738/"), ("Mega Adventures", "GBA", "Ruby", "Complete", "https://www.pokecommunity.com/threads/pokemon-mega-adventure.362058/"), ("Nightmare", "PC", None, "Complete", "https://www.pokecommunity.com/threads/pokémon-nightmare.504856/"), ("Poketale", "PC", None, "Complete", "https://www.mediafire.com/file/n6yzxnclf2uys7c/Poketale.zip/file"), ("Relict", "PC", None, "Complete", "https://phiongames.blogspot.com/2025/12/descarga-pokemon-relict.html"), ("Shattered Light", "PC", None, "Ongoing", "https://eeveeexpo.com/shattered-light/"), ("Sienna", "GBA", "Ruby", "Complete", "https://www.pokecommunity.com/threads/hack-of-the-year-2010-pokémon-sienna-complete-version-released.202372/"), ("Spades and Clubs", "GBA", "FireRed", "Beta", "https://senta-storage-system.neocities.org/patchrelease"), ("Tectonic", "PC", None, "Ongoing", "https://www.tectonic-game.com"), ("Vega Fairy EX", "GBA", "FireRed", "Complete", "https://www.pokecommunity.com/threads/pokemon-vega-fairy-edition-ex-minus-v1-4-balance-updates-and-skarmory-evo.475538/"), ] UPDATES = { "Coral": "roms/gbc/Hacks/Pokemon - Coral (Hack).gbc", "Giratina Strikes Back": "roms/gba/Hacks/Pokemon - Giratina Strikes Back Full (Hack).gba", } def norm(s): s = unicodedata.normalize("NFKD", s).encode("ascii", "ignore").decode().lower() s = re.sub(r"^pokemon\s+", "", s) return re.sub(r"[^a-z0-9]+", " ", s).strip() def yaml_quote(s): if s is None: return '"—"' if any(c in s for c in ':"\\#[]{}'): return json.dumps(s) return s def note_body(title, source): return textwrap.dedent(f"""\ # {title} > [!info] From curated YouTube/pastebin list import (2026-06-23) > Added to catalog from community recommendation lists. Download may be gated — see [[Wanted]]. ## Summary Surfaced from external recommendation lists (pastebin / Google Doc). Not yet in the RomM library unless noted in frontmatter. ## Links - Info / thread: {source} ## In the library > [!note] See `library_path` in frontmatter. [[Index|← back to directory]] """) def patch_library_path(path: Path, lp: str): text = path.read_text(encoding="utf-8") text = re.sub(r"^library_path:.*$", f'library_path: "{lp}"', text, count=1, flags=re.M) path.write_text(text, encoding="utf-8") def main(): existing = {norm(p.stem) for p in VAULT.glob("*.md")} today = date.today().isoformat() created = [] for name, platform, base, status, source in NEW: if norm(name) in existing: continue title = f"Pokémon {name}" tags = ["hack", f"platform/{platform.lower()}", f"status/{status.lower().replace(' ', '-')}"] if base: tags.append(f"base/{base.lower().replace(' ', '-')}") fm = [ "---", f"title: {title}", f'platform: {platform}', f'base: {base or "—"}', 'version: "—"', f"status: {status}", "type: [New Experience]", 'generation: "—"', 'library_path: "—"', f"source: {json.dumps(source)}", f"added: {today}", "play_status: Unplayed", "tags:", ] fm += [f" - {t}" for t in tags] fm.append("---") body = note_body(title, source) out = VAULT / f"{name}.md" out.write_text("\n".join(fm) + "\n" + body, encoding="utf-8") created.append(name) for stem, lp in UPDATES.items(): p = VAULT / f"{stem}.md" if p.exists(): patch_library_path(p, lp) print(f"Created {len(created)} notes: {', '.join(created)}") print(f"Updated library_path: {', '.join(UPDATES)}") if __name__ == "__main__": main()