For ROM hack developers
This page describes how ROM hack support is added to PKForge, what information we need from the hack's developers, and what each piece is used for.
Two ways a hack gets supported
| Your hack's save… | Support lives in | Examples |
|---|---|---|
| keeps an official layout (same blocks, same Pokemon format), with new data | the PKForge fork of PKHeX (sofianeelhor/PKForge-PKHeX): a save type, a personal table, item and form data | Luminescent Platinum (Brilliant Diamond), Compass (Scarlet / Violet) |
| uses a custom layout (CFRU, expansion engines, moved sections, new box storage) | a dedicated hack session in PKForge.Engine, which reads and writes the save itself | Unbound, Radical Red, GS Chronicles |
CFRU hacks share one engine (CfruEngineSession). A new CFRU hack is mostly a profile plus its data tables, which makes it the quickest kind to add.
What we need
Items 1–6 are required; the rest makes support complete.
1. Engine and base
Which base game and engine the hack is built on (FireRed + CFRU, pokeemerald-expansion, pokefirered, Brilliant Diamond, Scarlet / Violet…), with the engine version or commit.
Why: it decides which of the two routes above applies and whether an existing engine can be reused.
2. A way to recognise your save
A value only your hack writes: a custom sector signature, a distinctive file size, or a marker block.
Why: PKForge identifies games from the save bytes, never the file name, and hack saves contain no readable game title. Unbound (0x01121999) and GS Chronicles (0x66290096) have their own sector signature; Luminescent Platinum is recognised by size and header; Radical Red has no signature and needs structural checks, which are slower to get right.
3. The save layout
The sector / section map, and where these live: party count and party, trainer name and IDs, money, security key, Pokédex seen / caught, every box (including boxes stored outside the usual PC storage) and the box count, bag pockets with their offsets and capacities.
Why: this is what PKForge reads and writes. A box stored in an unexpected sector is a box that would otherwise be invisible.
4. The checksum scheme
Checksum algorithm, the size of the window checked for each section, footer fields, and any region written outside the checked sections.
Why: PKForge recomputes checksums on every write. A wrong window corrupts the save; on vanilla windows, GS Chronicles would have broken four sections.
5. The Pokemon format
Party and box structure size, encryption (plaintext or the official XOR), substructure order, bit packing (species width, shiny rule…) and the per-Pokemon checksum.
Why: it is how each Pokemon is decoded, edited and written back byte-for-byte.
6. ID tables, with names
Species (including form slots), moves, items and abilities, as the IDs stored in the save plus their English names. Source files or a generator are ideal: GS Chronicles' tables are produced by a script pinned to an exact commit of the hack's source, so they can be regenerated identically.
Why: names drive the UI, and matching names to official species is what gives your Pokemon their sprites, stats and a correct .pk3 export.
7. Pokédex numbering
How species map to Pokédex bits, and your custom dex slots.
8. Base stats
Stats, types, gender ratio, abilities (1 / 2 / hidden) and growth rate, at least for species that do not exist in official games.
Why: without them, original species get neutral placeholder stats.
9. Move PP
The base PP of every move, including new ones.
10. Bag pockets
Which pocket each item belongs to.
11. Forms
Form names and their index order for each species, and form counts.
12. Sprites and icons
For new species and forms, under a license that allows redistribution. Otherwise PKForge falls back to official sprites.
13. Text encoding
Any change to the character table. By default the Gen 3 table is assumed for GBA hacks.
14. Sample saves
Saves at several points: a fresh save, mid-game, and a late save with full boxes (every box, including extra ones), a full bag, a filled Pokédex and custom forms. One per version you want supported.
Why: they are used for validation tests: PKForge must read them, edit them and re-read them unchanged. They stay on the maintainer's machine and are never published.
15. Versioning
Whether IDs or the layout change between your releases, and which versions exist.
Why: Luminescent Platinum 1.1 and 1.3 have different save sizes; Radical Red's tables are pinned to 4.1.
16. Permission
Your permission to redistribute the data and art, the license, and the name you want credited.
Why: we only include what we are allowed to redistribute. Data contributors are thanked by name in the release notes and in Credits.
Not needed
Encounter tables and learnsets. ROM hack saves get no legality verdict, so PKForge does not use them.
How to send it
- Open an issue on GitHub titled ROM hack support: <your hack>, or reach us on Discord.
- Link your source repository if it is public, or attach the tables.
- Send sample saves privately (Discord DM), not in a public issue.
What happens next
- We check the layout and pick the route.
- We add detection, then the tables, then the read and write paths.
- Your sample saves become tests: read, edit, write, re-read, byte-identical when nothing changed.
- Until support is released, a suspected hack may offer Browse read-only or Edit at my own risk. Known unsafe layouts remain read-only, and some unsupported CFRU game choices refuse writes outright. The risk choice does not add support (see ROM hacks).
- Support is released in an update, with credit to you.
For contributors writing the code
A new CFRU hack follows the GS Chronicles pattern:
- Add
src/PKForge.Engine/<Hack>/Data/*as embedded resources. - Implement
ICfruGameData(names, national ID bridges, dex numbering, PP, pockets, base stats). - Add a
CfruEngineSessionsubclass with aCfruGameProfile(name, tag, data provider, extra sector boxes, detection). - Add the detector in
SaveParserand route it inSaveEngine. - Add the generation branch in
LegalizerService, the game art, and tests with the sample saves.
A hack on an official layout is a change in the PKHeX fork instead (save type, detection in SaveUtil, personal table, item and form data), on its own pkforge/* branch, followed by a submodule bump in PKForge.
See Contributing for the build and test setup.

