Table of Contents
The cartkit CLI
tools/cartkit.py is the cart-author command line: scaffold, validate,
pin, pack, add-release-workflow, selftest. Python 3, no other
dependencies -- unlike modkit it never drives the engine,
so it runs anywhere.
python3 tools/cartkit.py <subcommand> [args]
Global flags: --repo PATH (repo root override), --json, --quiet.
A cart repo holds cart.json, label.png, a README.md, a
CHANGELOG.md and a release workflow. The built .g1rcart is an output,
not a source file -- see Custom Carts for what that
file is.
scaffold
python3 tools/cartkit.py scaffold my_cart
python3 tools/cartkit.py scaffold my_cart --base gold --seal sealed+ \
--title "My Cart" --author you --github owner/my-cart
Writes a new cart repo with a cart.json carrying a placeholder pin, a
generated label.png in the shell colour, and the release workflow.
Flags: --title (1..48 characters), --author, --summary (<=120),
--base red|blue|yellow|gold|silver|crystal, --shell "#rrggbb",
--seal sealed|sealed+|open, --github owner/repo, --into DIR,
--force.
Replace the placeholder pin with a real one before validating; pin
drops it for you.
pin
python3 tools/cartkit.py pin my_cart owner/repo@1.2.3
python3 tools/cartkit.py pin my_cart https://gamebanana.com/mods/512345
python3 tools/cartkit.py pin my_cart owner/repo@1.2.3 --option difficulty=hard
Adds or updates one pin and resolves its hash for you. For a GitHub
spec it reads the release's own sha256sums.txt and records the hash of
the archive that release actually publishes. Do not copy a hash from a
locally packed zip: a mod's release workflow rewrites the version inside
the archive, so a local build of the same version hashes differently and
the pin will never verify.
Flags: --id ID (the mod's id in the cart, if it cannot be derived),
--file FILE (GameBanana file id when the mod publishes several),
--option KEY=VALUE (freeze an option; true/false and numbers are
parsed), --clear-options.
Frozen options ride the cart and are part of its hash, so a cart's mods arrive configured.
validate
python3 tools/cartkit.py validate my_cart
python3 tools/cartkit.py validate my_cart --online --strict
Offline by default: shape, vocabulary and internal consistency. --online
resolves every pin against GitHub and GameBanana and checks the recorded
hashes against what those releases publish, which is the check that
catches a stale pin.
| Rule | Refuses |
|---|---|
| CK001 | no cart.json in that directory |
| CK002 | a field missing, malformed, or outside its vocabulary |
| CK003 | label art that is not a .png |
| CK004 | a mods list that is not 1 to 64 pins |
| CK005 | a load_order that does not match the pins |
| CK100 | a GitHub pin that does not resolve to a release |
| CK101 | a GitHub pin whose sha256 disagrees with the published archive |
| CK110 | a GameBanana pin that does not resolve |
| CK111 | a GameBanana pin whose md5 disagrees with the published file |
--no-download stops validate fetching a release asset purely to hash
it; a pin it could not check is reported as unchecked rather than passed.
cartkit packs only the fields it documents, so an unknown field warns
rather than being silently carried. If you add a field to the cart format,
teach cartkit about it in the same change or it will be dropped from every
packed cart.
pack
python3 tools/cartkit.py pack my_cart
Writes <id>-<version>.g1rcart into the working directory: the manifest,
the pins and the label PNG base64'd inline. --online and
--no-download behave as they do for validate.
add-release-workflow
python3 tools/cartkit.py add-release-workflow my_cart
Copies in .github/workflows/release.yml, which on every push to main
fetches cartkit, runs selftest, validates the cart --online --strict,
packs it, and publishes the .g1rcart with a sha256sums.txt. A version
that already has a release is skipped rather than rebuilt, so cutting a
release is bumping version in cart.json.
The workflow fetches cartkit from the engine repo at
CARTKIT_REPO/CARTKIT_REF, which is how a cart repo stays in step with
the format without vendoring the tool.
selftest
python3 tools/cartkit.py selftest
Runs cartkit's own checks, including a scaffold/validate/pack round trip and a determinism check on the generated label. The release workflow runs this before it trusts a fetched copy of the tool.
A cart from nothing
python3 tools/cartkit.py scaffold my_cart --base red --author you \
--github owner/my-cart
python3 tools/cartkit.py pin my_cart owner/some-mod@1.0.0
python3 tools/cartkit.py validate my_cart --online
python3 tools/cartkit.py pack my_cart
Then push the repo, let the workflow cut the release, and add an entry
under carts/ in the mod index. The index entry's
repo names the cart's repo, not the repo of a mod it pins.
Start here
Concepts
Tutorials
- The ladder
- 01 Sprite and Text Tweak
- 02 Balance Patch
- 03 New Species
- 04 New Item and Ball
- 05 New Move
- 06 NPC and Dialogue
- 07 New Map
- 08 Quest
- 09 Custom Music
- 10 New Mechanic
- 11 Custom UI Screen
- 12 Mini Total Conversion
Reference
Guides
- Art Pipeline
- Audio Authoring
- Preparing Your Mod for Gen 2
- Translations
- Total Conversions
- Link Compatibility
- Publishing a Mod
- Custom Carts
- Docs Style Guide
Playing
Engine 1.0.0 · mod API 2 · repository · Discord
This project ships no ROM data. All game content is decoded on the player's machine from their own verified Pokemon Red ROM; mods ship recipes and original assets, never extracted content.