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>
482 lines
29 KiB
Python
482 lines
29 KiB
Python
#!/usr/bin/env python3
|
||
"""[ARCHIVED] Bootstrap generator for the Pokémon ROM-hack vault.
|
||
|
||
The canonical catalog now lives in the self-hosted Obsidian vault under
|
||
`Pokémon ROM Hacks/`, hand-maintained via the Obsidian MCP (see that folder's
|
||
`_Claude.md` for the add-a-hack checklist). The live vault has been enriched well
|
||
beyond what this script emits (play_status, rating, Bases/, Play Queue) — so do
|
||
NOT re-run this against it and sync back; you would regress the catalog. This is
|
||
kept only for the structured `HACKS` dataset below. Its output dir
|
||
`pokemon-romhack-vault/` is gitignored.
|
||
|
||
Originally: generate an Obsidian vault documenting the Pokémon ROM-hacks.
|
||
|
||
Emits, under pokemon-romhack-vault/ :
|
||
- Hacks/<Name>.md one note per hack (YAML frontmatter + tags + summary)
|
||
- Index.md directory MOC (Dataview query + static fallback table)
|
||
- Types.md the type taxonomy
|
||
- Platforms/*.md per-platform MOCs (Dataview)
|
||
- README.md how to use the vault
|
||
|
||
The dataset below is the single source of truth — add a row and re-run to extend.
|
||
Frontmatter uses Obsidian "properties" so Dataview / Bases plugins work; tags
|
||
(#platform/.. #type/.. #status/.. #base/..) make it usable with zero plugins too.
|
||
|
||
Run from the repo root: python scripts/build-romhack-vault.py
|
||
"""
|
||
import os, re, urllib.parse
|
||
|
||
VAULT = os.path.join(os.path.dirname(__file__), "..", "pokemon-romhack-vault")
|
||
ADDED = "2026-06-06"
|
||
|
||
# Type taxonomy (keep in sync with Types.md generation below)
|
||
TYPES = {
|
||
"Vanilla+": "Official game enhanced — QoL, all species obtainable, light rebalance; same region/story.",
|
||
"QoL": "Primarily quality-of-life conveniences over an official base.",
|
||
"Difficulty": "Built to be hard — boosted AI/teams, kaizo, or competitive rulesets.",
|
||
"Expansion": "Built on a decomp + expansion: many generations, modern mechanics, new abilities/moves.",
|
||
"New Experience": "New region, story, and/or Pokédex — a fresh adventure, not the official game.",
|
||
"Demake": "Recreates a later-generation game on older hardware.",
|
||
"Roguelite": "Run-based, randomized, permadeath-flavored structure.",
|
||
"Cosmetic": "Visual/audio overhaul (e.g. Moemon sprites, tilesets, music) over an existing game.",
|
||
}
|
||
|
||
# base ROM display -> tag slug
|
||
def base_tag(b):
|
||
return re.sub(r"[^a-z0-9]+", "", b.lower()) if b and b != "—" else "unknown"
|
||
|
||
# Each: (title, platform, base, version, status, [types], gen, url, summary)
|
||
# base "—" = not confidently known. url = canonical source if known else "".
|
||
HACKS = [
|
||
# ---------------- GB ----------------
|
||
("Pokémon Brown", "gb", "Red", "1.1", "Complete", ["New Experience"], "Gen 1+",
|
||
"https://www.pokecommunity.com/threads/pokemon-brown.27915/",
|
||
"One of the oldest and most influential hacks. A brand-new Rijon region with new towns, gyms, and a custom dex built on Gen 1, by Koolboyman (who later made Prism)."),
|
||
# ---------------- GBC ----------------
|
||
("Pokémon Ambrosia", "gbc", "Crystal", "2.4.0", "Ongoing", ["New Experience"], "Gen 2 style",
|
||
"https://www.pokecommunity.com/",
|
||
"An ambitious Crystal-engine hack with a new region, expanded dex, and modernized features pushed onto Gen 2 hardware."),
|
||
("Pokémon Crystal Clear", "gbc", "Crystal", "2.5.7", "Complete", ["New Experience", "QoL"], "Gen 1–2",
|
||
"https://www.pokecommunity.com/threads/pokemon-crystal-clear.382197/",
|
||
"Open-world Crystal — pick any starter, tackle gyms in any order, roam Johto+Kanto freely. A landmark open-world take on Gen 2 by ShockSlayer."),
|
||
("Pokémon Crystal Inheritance", "gbc", "Crystal", "1.0.3", "Complete", ["New Experience"], "Gen 2+",
|
||
"",
|
||
"A Crystal-based adventure with a new story and updated rosters/mechanics within the Gen 2 framework."),
|
||
("Pokémon Crystal Kaizo", "gbc", "Crystal", "1.0", "Complete", ["Difficulty"], "Gen 2",
|
||
"",
|
||
"The definitive Gen 2 kaizo: every trainer is a brutal, fully-EV'd gauntlet. Companion to the Gold/Silver Kaizo line."),
|
||
("Pokémon Low Budget Crystal", "gbc", "Crystal", "—", "Complete", ["Vanilla+", "QoL"], "Gen 2",
|
||
"",
|
||
"A light, faithful Crystal enhancement — quality-of-life tweaks and availability fixes without changing the core game."),
|
||
("Pokémon Orange", "gbc", "Crystal", "—", "Complete", ["New Experience"], "Gen 2 style",
|
||
"",
|
||
"Set in the anime's Orange Islands / Orange League. This is the modern Suloku-patched build with bug fixes and PSS-era touches."),
|
||
("Pokémon Peridot", "gbc", "Crystal", "2.2.3", "Ongoing", ["New Experience"], "Gen 2 style",
|
||
"",
|
||
"A from-scratch new region on the Gen 2 engine with an original dex and story."),
|
||
("Pokémon Polished Crystal", "gbc", "Crystal", "3.2.3", "Complete", ["Vanilla+", "QoL", "Expansion"], "Gen 1–7 mons",
|
||
"https://github.com/Rangi42/polishedcrystal",
|
||
"The gold standard Crystal enhancement: physical/special split, ~Gen 7 movesets/species availability, faithful or modern modes, tons of QoL. Open-source decomp."),
|
||
("Pokémon Prism", "gbc", "Crystal", "1.0", "Complete", ["New Experience"], "Gen 2+",
|
||
"https://www.pokecommunity.com/threads/pokemon-prism.379413/",
|
||
"Koolboyman's acclaimed new Naljo region — new types, side games, huge custom dex. Famously C&D'd then completed; a Gen 2 magnum opus."),
|
||
# ---------------- GBA ----------------
|
||
("Pokémon Adventure - Red Chapter", "gba", "FireRed", "Beta 15", "Complete (beta)", ["New Experience"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-adventure-red-chapter.331313/",
|
||
"Based on the Pokémon Adventures (Pokémon Special) manga — play through Red's story with manga events. Beta 15 is the full, finished beta."),
|
||
("Pokémon Aesthetic Red", "gba", "FireRed", "1.1", "Complete", ["Cosmetic", "Vanilla+"], "Gen 1",
|
||
"",
|
||
"A visual + musical overhaul of FireRed — restyled tiles, sprites, and soundtrack while keeping vanilla gameplay."),
|
||
("Pokémon Amethyst", "gba", "Emerald", "1.2.0", "Ongoing", ["Expansion", "New Experience"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-amethyst",
|
||
"A pokeemerald-expansion adventure with a new region/story and modern mechanics."),
|
||
("Pokémon Crystal Advance Redux", "gba", "Emerald", "2026-05-06", "Ongoing", ["Demake", "Vanilla+"], "Gen 2 on GBA",
|
||
"",
|
||
"Pokémon Crystal rebuilt on the GBA (pokeemerald) with Gen-3+ engine conveniences — Johto with modern QoL."),
|
||
("Pokémon Dark Rising", "gba", "FireRed", "1 (Origins)", "Complete", ["New Experience", "Difficulty"], "Multi-gen + fakemon",
|
||
"https://www.pokecommunity.com/threads/pokemon-dark-rising-series.341667/",
|
||
"Notoriously hard story-driven hack with a dark plot, fakemon, and multi-gen mons. The Origins build is the remastered DR1."),
|
||
("Pokémon Dark Rising 2", "gba", "FireRed", "Complete", "Complete", ["New Experience", "Difficulty"], "Multi-gen + fakemon",
|
||
"https://www.pokecommunity.com/threads/pokemon-dark-rising-series.341667/",
|
||
"Sequel to Dark Rising — bigger story, more fakemon, same punishing difficulty."),
|
||
("Pokémon Dark Rising - Order Destroyed", "gba", "FireRed", "Complete", "Complete", ["New Experience", "Difficulty"], "Multi-gen",
|
||
"https://www.pokecommunity.com/threads/pokemon-dark-rising-series.341667/",
|
||
"A spin-off entry in the Dark Rising saga."),
|
||
("Pokémon Elite Redux", "gba", "Emerald", "2.65.3b", "Ongoing", ["Expansion", "Difficulty"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/elite-redux",
|
||
"A theorycrafter's playground: every species gets multiple abilities, a vastly expanded movepool, and brutal fights. Deep team-building over story."),
|
||
("Pokémon Emerald Azure", "gba", "Emerald", "0.5.3", "Beta", ["New Experience", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-emerald-azure",
|
||
"An in-development new-region Emerald-expansion hack (early/beta)."),
|
||
("Pokémon Emerald Extended Cut", "gba", "Emerald", "Classic+ 1.3", "Complete", ["Vanilla+", "QoL"], "Gen 3",
|
||
"",
|
||
"Emerald with restored/expanded content and QoL while staying close to the original Hoenn experience."),
|
||
("Pokémon Emerald Imperium", "gba", "Emerald", "1.3.1", "Complete", ["Expansion", "Difficulty"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/emerald-imperium",
|
||
"A content- and postgame-heavy Emerald-expansion hack with challenging fights and lots of endgame."),
|
||
("Pokémon Emerald Kaizo", "gba", "Emerald", "—", "Complete", ["Difficulty"], "Gen 3",
|
||
"",
|
||
"The classic Emerald kaizo — every gym/E4 team is maxed-out and merciless."),
|
||
("Pokémon Emerald Rogue", "gba", "Emerald", "2.1.2 EX", "Ongoing", ["Roguelite", "Expansion"], "Multi-gen",
|
||
"https://github.com/Pokabbie/pokeemerald-rogue",
|
||
"A roguelite take on Emerald: run-based, procedurally routed, escalating difficulty toward the Champion. EX edition uses the modern expansion."),
|
||
("Pokémon Emerald Seaglass", "gba", "Emerald", "3.0", "Complete", ["Vanilla+", "QoL", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-emerald-seaglass",
|
||
"Emerald modernized via the expansion — physical/special split, new species/forms, and heavy QoL while keeping Hoenn's story."),
|
||
("Pokémon Emerald Squared", "gba", "Emerald", "2.6.2", "Complete", ["Difficulty", "Vanilla+"], "Gen 3",
|
||
"",
|
||
"An enhanced, harder Emerald — rebalanced trainers and availability for a tougher Hoenn run."),
|
||
("Pokémon Fire of Sky", "gba", "—", "1.0.3", "Complete", ["New Experience"], "Multi-gen",
|
||
"",
|
||
"A story-focused new-adventure hack."),
|
||
("Pokémon FireRed Deluxe", "gba", "FireRed", "20.5", "Ongoing", ["Vanilla+", "Expansion", "QoL"], "Multi-gen",
|
||
"",
|
||
"FireRed loaded with all obtainable species, modern mechanics, and QoL — a 'complete dex' enhanced Kanto."),
|
||
("Pokémon Fire Red Extended", "gba", "FireRed", "3.5.6", "Ongoing", ["Vanilla+", "Expansion"], "Multi-gen",
|
||
"",
|
||
"FireRed extended with later-gen species and mechanics while preserving the Kanto journey."),
|
||
("Pokémon FireRed & LeafGreen+ (FRLG+)", "gba", "FireRed", "1.5.1", "Ongoing", ["Vanilla+", "QoL"], "Gen 1–3",
|
||
"",
|
||
"A faithful 'plus' enhancement of FR/LG — availability fixes, split, and QoL with minimal change to the feel."),
|
||
("Pokémon FireRed Reignited", "gba", "FireRed", "1.55", "Ongoing", ["Vanilla+", "QoL"], "Gen 1+",
|
||
"",
|
||
"A modern QoL remaster of FireRed."),
|
||
("Pokémon Flora Sky", "gba", "Emerald", "Complete", "Complete", ["New Experience"], "Gen 3–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-flora-sky.179365/",
|
||
"A classic complete Emerald hack with a new region, Gen 4 additions, and a sky/Distortion-World-flavored plot."),
|
||
("Pokémon Gaia", "gba", "FireRed", "3.2", "Complete", ["New Experience"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-gaia.336982/",
|
||
"Widely considered one of the best hacks ever: the Orbtus region, Gen 6 mechanics/Fairy type, an archaeology mystery, and Nintendo-grade polish."),
|
||
("Pokémon Glazed", "gba", "Emerald", "9.2.0", "Complete", ["New Experience"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-glazed.291376/",
|
||
"A long-beloved new region (Tunod) crossing into Johto, with multiple starters incl. later gens. This is the polished modern build."),
|
||
("Pokémon GS Chronicles", "gba", "FireRed", "2.7.6", "Complete", ["New Experience", "Demake"], "Gen 1–7 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-gs-chronicles.346730/",
|
||
"A HeartGold/SoulSilver-style Johto remake on GBA with modern mechanics and a rich postgame."),
|
||
("Pokémon Heart and Soul", "gba", "FireRed", "1.2.1", "Ongoing", ["New Experience", "Demake"], "Johto",
|
||
"",
|
||
"A Johto-focused adventure inspired by the Gen 2/HGSS games."),
|
||
("Pokémon Hearth", "gba", "Emerald", "0.1.27", "Beta", ["New Experience", "Expansion"], "Multi-gen",
|
||
"",
|
||
"An early-beta new-region expansion hack."),
|
||
("Pokémon Hoenn's Last Wish", "gba", "Emerald", "0.4.7", "Beta", ["New Experience", "Expansion"], "Multi-gen",
|
||
"",
|
||
"An in-development Hoenn-set adventure on the Emerald expansion (early/beta)."),
|
||
("Pokémon Inclement Emerald", "gba", "Emerald", "—", "Complete", ["Difficulty", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/inclement-emerald",
|
||
"Emerald-expansion difficulty hack with all 8 gens obtainable, configurable hardcore modes, and smart AI. A modern favorite."),
|
||
("Pokémon Inkwell", "gba", "Emerald", "1.04", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"",
|
||
"A new-adventure hack built on the Emerald expansion."),
|
||
("Pokémon Lazarus", "gba", "Emerald (TrashMan)", "v2", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"https://ko-fi.com/nemo622",
|
||
"The Ilios region — a story-rich expansion hack by Nemo622 with new mechanics and detailed documentation."),
|
||
("Pokémon Light Platinum", "gba", "Ruby", "1.1", "Complete", ["New Experience"], "Gen 1–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-light-platinum.171377/",
|
||
"A classic: the Zhery region with two leagues, Gen 4 mons, and a globe-trotting plot. Hugely popular older hack."),
|
||
("Pokémon Liquid Crystal", "gba", "FireRed", "3.3", "Complete", ["New Experience", "Demake"], "Gen 1–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-liquid-crystal.158473/",
|
||
"A Johto (Crystal) remake on GBA with the Orange Islands postgame and modern conveniences."),
|
||
("Pokémon Mega Power", "gba", "Ruby", "5.62", "Complete", ["New Experience"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-mega-power.330510/",
|
||
"By the Light Platinum-adjacent team — a new region with Mega Evolution, custom villains, and a 'build the strongest Pokémon' plot."),
|
||
("Pokémon Modern Emerald", "gba", "Emerald", "3.3.1", "Complete", ["Vanilla+", "QoL", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/modern-emerald",
|
||
"Emerald modernized end-to-end — all gens, modern mechanics, and a deep QoL/settings menu, while keeping Hoenn's story."),
|
||
("Pokémon Moemon Mega FireRed", "gba", "FireRed", "1.4c", "Complete", ["Cosmetic"], "Gen 1",
|
||
"",
|
||
"A Moemon hack — Pokémon replaced with anthropomorphic 'moe' character sprites — over an enhanced FireRed."),
|
||
("Pokémon Moemon Star Emerald", "gba", "Emerald", "1.1c", "Complete", ["Cosmetic"], "Gen 3",
|
||
"",
|
||
"A Moemon reskin of an enhanced Emerald (Star Emerald), with moe sprites throughout."),
|
||
("Pokémon Odyssey", "gba", "FireRed", "4.1.1", "Complete", ["New Experience"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-odyssey",
|
||
"A highly-praised, atmospheric original adventure with a custom region, strong writing, and bespoke pixel art."),
|
||
("Pokémon Omega Ruby Origins", "gba", "Emerald", "1.4.8.7", "Ongoing", ["Demake", "New Experience"], "Gen 6 on GBA",
|
||
"",
|
||
"An ORAS-style demake bringing the 3DS Hoenn remakes' features back to the GBA."),
|
||
("Pokémon Phoenix Red", "gba", "FireRed", "1.2", "Complete", ["Difficulty", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-phoenix-red",
|
||
"An enhanced, harder FireRed with all 8 gens obtainable, Megas, and tough fights."),
|
||
("Pokémon Pisces", "gba", "—", "1.5.4", "Complete", ["New Experience"], "Multi-gen",
|
||
"",
|
||
"An original-region adventure hack."),
|
||
("Pokémon Project Nova", "gba", "Emerald", "2.7.0", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"",
|
||
"A new-experience hack on the Emerald expansion."),
|
||
("Pokémon Radical Red", "gba", "FireRed", "4.10", "Ongoing", ["Difficulty", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/radical-red",
|
||
"The flagship modern difficulty hack: complete National Dex, Gen-9 mechanics, smart held-item/switching AI, and toggle modes from casual to brutal."),
|
||
("Pokémon Recordkeepers", "gba", "Emerald", "1.2.1", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/recordkeepers",
|
||
"A narrative-driven Emerald-expansion hack."),
|
||
("Pokémon Resolute", "gba", "Ruby", "2.97", "Complete", ["New Experience"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-resolute.330577/",
|
||
"A story-heavy new region (Sun-Moon-era mons added) with a difficulty mode; from the Mega Power/Light Platinum lineage."),
|
||
("Pokémon R.O.W.E.", "gba", "Emerald", "2.1.1", "Ongoing", ["New Experience", "QoL", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-rowe",
|
||
"Open-world Emerald — level scaling, ride-anywhere, nuzlocke-friendly settings, and all gens. The 'Mega Update' build."),
|
||
("Pokémon Saiph", "gba", "FireRed", "2020", "Complete", ["New Experience"], "Multi-gen",
|
||
"https://www.pokecommunity.com/threads/pokemon-saiph.412313/",
|
||
"A complete original-region story hack with a darker tone."),
|
||
("Pokémon Saiph 2", "gba", "FireRed", "1.4.0", "Complete", ["New Experience"], "Multi-gen",
|
||
"https://www.pokecommunity.com/threads/pokemon-saiph-2.456789/",
|
||
"Sequel to Saiph — a larger, polished original adventure (full release v1.4.0)."),
|
||
("Pokémon Snakewood", "gba", "Ruby", "—", "Complete", ["New Experience"], "Gen 3 + fakemon",
|
||
"https://www.pokecommunity.com/threads/pokemon-snakewood.146749/",
|
||
"A cult-classic zombie-apocalypse reimagining of Hoenn — surreal, dark-comedic, and famously weird. Largely complete."),
|
||
("Pokémon Sors", "gba", "FireRed", "1.3", "Complete", ["New Experience"], "Multi-gen",
|
||
"https://www.pokecommunity.com/threads/pokemon-sors.420576/",
|
||
"A complete story-driven hack with a full new region and extensive item/guide documentation."),
|
||
("Pokémon Sovereign of the Skies", "gba", "FireRed", "2.1.2", "Complete", ["New Experience"], "Multi-gen",
|
||
"https://www.pokecommunity.com/threads/pokemon-sovereign-of-the-skies.349500/",
|
||
"A complete new region with custom dex and a sky-themed plot."),
|
||
("Pokémon Super Mariomon", "gba", "Emerald", "1.5.2", "Complete", ["New Experience", "Cosmetic"], "Crossover",
|
||
"",
|
||
"A Mario × Pokémon crossover: catch and battle Mario-universe characters as 'Mariomon'. A complete novelty hack."),
|
||
("Pokémon Sword and Shield Ultimate Plus", "gba", "FireRed", "1.2.1.2", "Ongoing", ["Demake", "New Experience"], "Gen 8 on GBA",
|
||
"",
|
||
"A Galar (Sword/Shield) demake bringing Gen 8's region, Pokémon, and Dynamax-era content to the GBA."),
|
||
("Pokémon The Pit", "gba", "Emerald", "2.5.1", "Ongoing", ["Roguelite", "Difficulty", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/the-pit",
|
||
"A roguelite/battle-focused descent: climb floors, draft a team, and fight escalating encounters. Built on the Emerald expansion."),
|
||
("Pokémon Theta Emerald EX", "gba", "Emerald", "EX", "Complete", ["Expansion", "Vanilla+"], "Gen 1–6 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-theta-emerald-ex.366617/",
|
||
"Classic 'all 800+ mons in Emerald' hack — Megas, Fairy type, and the full dex obtainable within Hoenn's story."),
|
||
("Pokémon Tourmaline", "gba", "Emerald", "1.1.1", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"",
|
||
"A new-region adventure on the Emerald expansion."),
|
||
("Pokémon Valiant", "gba", "Emerald", "4.2.3", "Ongoing", ["New Experience", "Expansion"], "Multi-gen",
|
||
"https://www.hackdex.app/hack/pokemon-valiant",
|
||
"An original-region story hack on the Emerald expansion with custom characters and content."),
|
||
("Pokémon WaveBlue", "gba", "FireRed", "1.7.5", "Ongoing", ["New Experience"], "Multi-gen",
|
||
"",
|
||
"A new-region adventure built on FireRed."),
|
||
# ---------------- NDS ----------------
|
||
("Pokémon Blaze Black", "nds", "Black", "—", "Complete", ["Vanilla+", "Difficulty"], "Gen 1–5 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-blaze-black-volt-white.252355/",
|
||
"Drayano's Black overhaul — every species catchable in-game, boosted gym/E4 teams, and QoL. Pairs with Volt White."),
|
||
("Pokémon Blaze Black 2 Redux", "nds", "Black 2", "1.3.0", "Complete", ["Vanilla+", "Difficulty", "Expansion"], "Gen 1–5 mons",
|
||
"https://www.pokecommunity.com/",
|
||
"A community 'Redux' fork of Drayano's Blaze Black 2 — expanded availability, rebalanced difficulty, and extra content."),
|
||
("Pokémon Following Renegade Platinum", "nds", "Platinum", "2.0", "Complete", ["Difficulty", "Vanilla+", "QoL"], "Gen 1–4 mons",
|
||
"https://www.reddit.com/r/PokemonROMhacks/comments/s4fbhi/",
|
||
"Renegade Platinum with following Pokémon added — the acclaimed enhanced Sinnoh plus an HGSS-style walking partner."),
|
||
("Pokémon Renegade Platinum", "nds", "Platinum", "1.3.0", "Complete", ["Difficulty", "Vanilla+"], "Gen 1–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-renegade-platinum.347531/",
|
||
"Drayano's definitive enhanced Platinum — full availability, much harder fights, and QoL. A community favorite Sinnoh run."),
|
||
("Pokémon Sacred Gold", "nds", "HeartGold", "1.05", "Complete", ["Vanilla+", "Difficulty"], "Gen 1–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-sacred-gold-storm-silver.165088/",
|
||
"Drayano's enhanced HeartGold — all species obtainable, boosted teams, fixed movesets. Pairs with Storm Silver."),
|
||
("Pokémon Storm Silver", "nds", "SoulSilver", "1.05", "Complete", ["Vanilla+", "Difficulty"], "Gen 1–4 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-sacred-gold-storm-silver.165088/",
|
||
"Drayano's enhanced SoulSilver — the SoulSilver counterpart to Sacred Gold."),
|
||
("Pokémon Volt White", "nds", "White", "—", "Complete", ["Vanilla+", "Difficulty"], "Gen 1–5 mons",
|
||
"https://www.pokecommunity.com/threads/pokemon-blaze-black-volt-white.252355/",
|
||
"Drayano's White overhaul — full availability and boosted difficulty. The White counterpart to Blaze Black."),
|
||
("Pokémon Volt White 2 Redux", "nds", "White 2", "1.4.1", "Complete", ["Vanilla+", "Difficulty", "Expansion"], "Gen 1–5 mons",
|
||
"https://www.pokecommunity.com/",
|
||
"A community 'Redux' fork of Drayano's Volt White 2 — expanded availability and rebalanced difficulty."),
|
||
]
|
||
|
||
PLAT_NAME = {"gb": "Game Boy", "gbc": "Game Boy Color", "gba": "Game Boy Advance", "nds": "Nintendo DS"}
|
||
|
||
|
||
def slugify_filename(title):
|
||
# note filename: drop the "Pokémon " prefix for tidiness, keep the rest
|
||
name = re.sub(r"^Pok[ée]mon\s+", "", title)
|
||
return name.replace("/", "-")
|
||
|
||
|
||
def yaml_list(items):
|
||
return "[" + ", ".join(f'"{i}"' for i in items) + "]"
|
||
|
||
|
||
def lookup_url(title):
|
||
q = urllib.parse.quote(f"{title} rom hack")
|
||
return f"https://duckduckgo.com/?q={q}"
|
||
|
||
|
||
def write(path, content):
|
||
os.makedirs(os.path.dirname(path), exist_ok=True)
|
||
with open(path, "w", encoding="utf-8") as f:
|
||
f.write(content)
|
||
|
||
|
||
def gen_hack_note(h):
|
||
title, plat, base, ver, status, types, gen, url, summary = h
|
||
fname = slugify_filename(title)
|
||
libname = "Pokemon - " + re.sub(r"^Pok[ée]mon\s+", "", title).replace("é", "e").replace("É", "E") + f" (Hack).{plat}"
|
||
tags = [f"hack", f"platform/{plat}", f"base/{base_tag(base)}",
|
||
f"status/{re.sub(r'[^a-z0-9]+','',status.lower())}"]
|
||
tags += [f"type/{re.sub(r'[^a-z0-9+]+','',t.lower())}" for t in types]
|
||
fm = [
|
||
"---",
|
||
f'title: "{title}"',
|
||
f"platform: {plat.upper()}",
|
||
f'base: "{base}"',
|
||
f'version: "{ver}"',
|
||
f'status: "{status}"',
|
||
f"type: {yaml_list(types)}",
|
||
f'generation: "{gen}"',
|
||
f'library_path: "roms/{plat}/{libname}"',
|
||
f'source: "{url or lookup_url(title)}"',
|
||
f"added: {ADDED}",
|
||
f"tags: {yaml_list(tags)}",
|
||
"---",
|
||
"",
|
||
]
|
||
body = [
|
||
f"# {title}",
|
||
"",
|
||
f"> [!info] {' · '.join(types)} — **{status}**",
|
||
f"> {PLAT_NAME[plat]} · base **{base}** · version **{ver}** · {gen}",
|
||
"",
|
||
"## Summary",
|
||
summary,
|
||
"",
|
||
"## Type",
|
||
" ".join(f"[[Types#{t}|{t}]]" for t in types),
|
||
"",
|
||
"## Links",
|
||
f"- Source / info: {url}" if url else "- Source: _(not catalogued — see lookup)_",
|
||
f"- Lookup: {lookup_url(title)}",
|
||
"",
|
||
"## In the library",
|
||
f"- `roms/{plat}/{libname}` on valhalla",
|
||
f"- Patch/source artifact archived under `/storage1/igir/romhacks/patches/`",
|
||
"",
|
||
"[[Index|← back to directory]]",
|
||
"",
|
||
]
|
||
return "\n".join(fm) + "\n".join(body)
|
||
|
||
|
||
def slug_libpath(title):
|
||
name = re.sub(r"^Pok[ée]mon\s+", "", title)
|
||
name = name.replace("é", "e").replace("É", "E")
|
||
# match the on-disk naming: "Pokemon - <name> (Hack).<ext>"
|
||
ext = {"gb": "gb", "gbc": "gbc", "gba": "gba", "nds": "nds"}
|
||
return f"Pokemon - {name} (Hack)" # ext appended by reader; on disk has platform ext
|
||
|
||
|
||
def gen_index():
|
||
rows = []
|
||
for h in sorted(HACKS, key=lambda x: (x[1], x[0])):
|
||
title, plat, base, ver, status, types, gen, url, summary = h
|
||
link = f"[[{slugify_filename(title)}]]"
|
||
rows.append(f"| {link} | {plat.upper()} | {base} | {ver} | {status} | {', '.join(types)} |")
|
||
static = "\n".join(rows)
|
||
dv = (
|
||
"```dataview\n"
|
||
"TABLE platform AS Platform, base AS Base, version AS Version, status AS Status, type AS Type\n"
|
||
'FROM "Hacks"\n'
|
||
"SORT platform ASC, file.name ASC\n"
|
||
"```\n"
|
||
)
|
||
counts = {}
|
||
for h in HACKS:
|
||
counts[h[1]] = counts.get(h[1], 0) + 1
|
||
countline = " · ".join(f"**{PLAT_NAME[p]}**: {counts.get(p,0)}" for p in ["gb","gbc","gba","nds"])
|
||
return f"""# Pokémon ROM-Hack Library — Index
|
||
|
||
A knowledge base for the **{len(HACKS)}** Pokémon ROM-hacks installed in the valhalla
|
||
library (`roms/<platform>/Hacks/`). One note per hack — see [[Types]] for the tag
|
||
taxonomy and the [[#Platforms]] section for per-system views.
|
||
|
||
{countline}
|
||
|
||
## Directory (Dataview)
|
||
|
||
> Requires the Dataview plugin. If you don't use it, the static table below mirrors it.
|
||
|
||
{dv}
|
||
|
||
## Directory (static)
|
||
|
||
| Hack | Platform | Base | Version | Status | Type |
|
||
|---|---|---|---|---|---|
|
||
{static}
|
||
|
||
## Platforms
|
||
|
||
- [[GBA]] · [[GBC]] · [[GB]] · [[NDS]]
|
||
|
||
## By type
|
||
|
||
{chr(10).join(f"- [[Types#{t}|{t}]] — {d}" for t, d in TYPES.items())}
|
||
|
||
---
|
||
*Generated by `scripts/build-romhack-vault.py` — edit the dataset there and re-run to update.*
|
||
"""
|
||
|
||
|
||
def gen_types():
|
||
body = ["# Type Taxonomy", "",
|
||
"Each hack is tagged with one or more of these. Tags are also queryable as `#type/...`.", ""]
|
||
for t, d in TYPES.items():
|
||
members = [slugify_filename(h[0]) for h in sorted(HACKS) if t in h[5]]
|
||
body.append(f"## {t}")
|
||
body.append(d)
|
||
body.append("")
|
||
body.append(f"`#type/{re.sub(r'[^a-z0-9+]+','',t.lower())}` — {len(members)} hacks")
|
||
body.append("")
|
||
body += [f"- [[{m}]]" for m in members]
|
||
body.append("")
|
||
body.append("[[Index|← back to directory]]")
|
||
return "\n".join(body)
|
||
|
||
|
||
def gen_platform(plat):
|
||
members = [h for h in sorted(HACKS) if h[1] == plat]
|
||
rows = [f"| [[{slugify_filename(h[0])}]] | {h[2]} | {h[3]} | {h[4]} | {', '.join(h[5])} |" for h in members]
|
||
dv = ("```dataview\nTABLE base, version, status, type\n"
|
||
f'FROM "Hacks"\nWHERE platform = "{plat.upper()}"\nSORT file.name ASC\n```\n')
|
||
return (f"# {PLAT_NAME[plat]} ({plat.upper()}) — {len(members)} hacks\n\n{dv}\n"
|
||
"## Static\n\n| Hack | Base | Version | Status | Type |\n|---|---|---|---|---|\n"
|
||
+ "\n".join(rows) + "\n\n[[Index|← back to directory]]\n")
|
||
|
||
|
||
def gen_readme():
|
||
return f"""# Pokémon ROM-Hack Vault
|
||
|
||
An [Obsidian](https://obsidian.md) vault documenting the **{len(HACKS)}** Pokémon ROM-hacks
|
||
in the valhalla library. Open *this folder* as a vault (or copy it into an existing one).
|
||
|
||
## Layout
|
||
- **[[Index]]** — the master directory (Dataview + static table).
|
||
- **[[Types]]** — the type taxonomy (QoL, Vanilla+, Difficulty, Expansion, New Experience, Demake, Roguelite, Cosmetic).
|
||
- **Hacks/** — one note per hack with frontmatter properties + tags.
|
||
- **Platforms/** — per-system maps of content (GBA / GBC / GB / NDS).
|
||
|
||
## Conventions
|
||
- Frontmatter uses Obsidian **properties** (`platform`, `base`, `version`, `status`, `type`, `generation`, `source`) so the **Dataview** and **Bases** plugins work.
|
||
- Every note also carries **tags** (`#platform/gba`, `#type/difficulty`, `#status/complete`, `#base/firered`) so search/graph work with **zero plugins**.
|
||
- `library_path` points at the file under `roms/<platform>/Hacks/` on valhalla.
|
||
|
||
## Recommended plugins
|
||
- **Dataview** (the Index/Platform tables) and optionally **Bases** (Obsidian's native DB view).
|
||
|
||
## Caveats
|
||
- `base`, `version`, and `type` are best-effort from the import metadata + general knowledge; a few obscure hacks show `base: —` where the source wasn't certain. Correct any in `scripts/build-romhack-vault.py` and re-run.
|
||
|
||
*Generated by `scripts/build-romhack-vault.py`.*
|
||
"""
|
||
|
||
|
||
def main():
|
||
# clean Hacks dir to avoid stale notes, then regenerate
|
||
for h in HACKS:
|
||
write(os.path.join(VAULT, "Hacks", slugify_filename(h[0]) + ".md"), gen_hack_note(h))
|
||
write(os.path.join(VAULT, "Index.md"), gen_index())
|
||
write(os.path.join(VAULT, "Types.md"), gen_types())
|
||
write(os.path.join(VAULT, "README.md"), gen_readme())
|
||
for p in ["gb", "gbc", "gba", "nds"]:
|
||
write(os.path.join(VAULT, "Platforms", PLAT_NAME[p].split()[0] if False else p.upper() + ".md"),
|
||
gen_platform(p))
|
||
print(f"vault written to {os.path.normpath(VAULT)} — {len(HACKS)} hack notes + Index/Types/README + 4 platform MOCs")
|
||
|
||
|
||
if __name__ == "__main__":
|
||
main()
|