The opensnes-* family replaces the earlier converters (gfx4snes, smconv, wav2brr, tmx2snes, font2snes, img2snes, palplan, aseprite2snes, sa1_patch) with a family of tools written for a studio shipping a game: one tool per function, the same conventions everywhere, settings that live beside the asset, no interpreter to install. This page is the contract every tool in the family follows; a tool that does not is a bug.
The earlier tools stay in the zip for one more release, each printing where its function went.
| Tool | Function |
|---|---|
| opensnes | the project: init, build, clean, run, test, doctor, upgrade (budget and release: later) |
| opensnes-sprite | sprite sheet or Aseprite export → tiles, palette, metasprite table, animation clips |
| opensnes-tileset | PNG → tileset and tilemap, deduplicated, with per-tile palettes and flips |
| opensnes-level | Tiled (later LDtk) → map, objects, collision |
| opensnes-text | bitmap font → text tiles (string tables, per language: after 1.0) |
| opensnes-palette | plan a project's palettes into CGRAM; quantize RGB art; preview |
| opensnes-image | full-screen pictures: HiColor; the Mode 7 perspective tables (Mode 7 pictures are opensnes-tileset --mode 7) |
| opensnes-sample | WAV → BRR |
| opensnes-music | Impulse Tracker → soundbank, with the samples it shares |
| opensnes-rom | finalize and check a ROM: header, size, mapper, checksum, the post-link checks, the credits |
| opensnes-save | battery save files (.srm): create, read, patch, compare (verifying a save format: with the one planned for 1.1) |
luna is not in the family: it is the emulator, debugger and test runner the family calls (opensnes run, opensnes test), documented on its own page (luna — command reference (pinned v1.38.0)).
Every message goes to stderr in one shape:
The tool, the file (and the tile, row, sample or frame when there is one), what is wrong, then what to do. A warning starts with warning: after the file. Nothing is said twice, nothing is said in uppercase.
The import settings of an asset live in a TOML file next to it, named after it: res/hero.png → res/hero.png.toml. The tool writes it the first time it runs with --save (recording the effective settings), reads it on every later run, and command-line options override it for that run. The artist sees the settings in their folder, under version control, and never opens the Makefile.
A composed asset — a soundbank made of several modules, a string table, a project palette, the ROM header — is not made from one source, so its settings file is named after what it produces and lists its sources: music/soundbank.toml with tool = "opensnes-music" and inputs = ["theme.it", "jingle.it"] under [bank], paths relative to the file. Every path in a settings file is relative to the file — palette = "town.pal" beside the picture it constrains — and --save writes them that way. opensnes-music bank music/soundbank.toml then builds it beside the file, and --save on a command-line run writes that file for you. One rule, two spellings: an asset with one source is named after its source (hero.png.toml), an asset with several is named after its product (soundbank.toml).
The first key is always tool: the build system's one generic rule reads it to know which tool converts the file. That rule exists (make/common.mk, ASSET_TOML): every *.toml and res/*.toml that names an opensnes-* tool is converted before the first C or ASM object (the tool's subcommand is the file's one table), and every <stem>_data.as the tools write is included by assets_gen.asm and assembled with the project. A fragment carries one ASSET_SECTION per blob (tiles, map, palette, sample), so the linker places each where it fits; a blob above 32 KB, the size of a bank, is cut in parts (<name>_tiles, <name>_tiles_1, …, each with _end). A project with settings files has no hand-written data.asm and no conversion rule in its Makefile: the starter/ is built this way. The rest is one table named after the subcommand, keys spelled exactly as the long options (--size is size). Unknown keys are refused (exit 1) so a typo cannot silently fall back to a default.
The formats the creator's tools write, as they write them: indexed or RGB PNG, Aseprite's JSON + sheet export, Tiled's .tmj, PCM WAV, Impulse Tracker .it. A tool quantizes or converts on the way in when the settings say so (opensnes-palette quantize is the explicit step for art that must be reviewed), and refuses, with the limit named, what the SNES cannot hold rather than silently masking it.
For contributors (the rule itself is .claude/rules/two_audiences.md):