A game-independent ROM-editing API.
(and a simple frontend for using it)
NOTE: Romtool is late alpha at best. It's probably usable, but I
don’t guarantee that it won’t explode the moment it’s off my system, nor
that this readme is up to date. Trust --help
more than this document.
Some people like to modify old games from the Age of Cartridges. Some games have tools available for exactly that, such as Lunar Magic for Super Mario World. Such tools are usually for some specific game and do not work for other games.
There are some tasks that are common to any game, though. For example, the format of binary data structures may differ from one ROM to another, but the act of digging that structure out and putting it in a form someone can use is universal. Reading a string is not fundamentally different from one game to another, either.
romtool is an attempt to collect game-independent functionality into an API and a very basic patch-making tool. The idea is to make simple hacks possible for non-programmers, and provide a useful set of primitives for more complex game-specific hacking tools.
Here are some things you can do with it that might be useful:
- Inspect ROM data tables, e.g. monster stats, dialogue strings, or spell lists. This is useful for creating guides or just understanding a game.
- Edit the same in a spreadsheet, then make a patch implementing the changes. This is useful for balance patches to RPGs and other statistics-heavy games.
- Version-control rom hacking projects. Romtool's dump format is TSV, and it can generate patches from several different text-based input formats. All are friendly to version control tools.
- Merge multiple existing patches together. Useful for players who want to pick and choose features from several unrelated hacks.
- See what an existing patch actually does. Useful for reverse engineering patches from authors who didn't document them.
- Examine and manipulate ROM headers. Useful for identifying or organizing ROMs.
Romtool isn't magic; it does need to be told where to find the data it is going to extract or patch. For this it needs a "rom map", which at the moment is a set of TSV files specifying the location and format of the data structures in the ROM, and the relationships between them. My hope is that maps for romtool can eventually serve as well-defined, machine-readable documentation of how different games' internals are structured.
There is a packaged installer available for Windows (FIXME: link here).
On Linux, for now, install from pip: pip install romtool
. Be aware
that you may need to have a C compiler installed for pip install
to
work, as romtool has at least one dependency (bitarray) that does not
provide pre-built wheels.
Sometime before v1.0, I intend to arrange .deb and .rpm packages. The main blocker for this are dependencies that do not themselves provide packages.
For development installs, see CONTRIBUTING.md.
The installation process should place the romtool
command somewhere in
your PATH, but I don't trust this yet and would like feedback. If
romtool --help
prints usage instructions, you're good to go.
NOTE: This tutorial uses 7th Saga for its examples, because at time of writing it has the most complete support. If you want to follow along, you will need a copy of the 7th Saga ROM, preferably in a directory by itself.
Before trying to do anything, you probably want to check that your ROM
is supported. romtool ident
will look at a ROM and print some
identifying information about it:
user@host:~$ romtool ident --long 7thsaga.rom.sfc
name: 7th Saga, The (USA)
file: 7thsaga.rom.sfc
type: snes
size: 1572864
crc32: B3ABDDE6
sha1: 8d2b8aea636a2239805c99744bf48c0b4df8d96e
md5: a42772a0beaf71f9256a8e1998bfe6e3
supported: yes
map: /path/to/romtool/maps/7th Saga, The(US)
For now, the important fields here are name
, supported
, and map
.
The name
field will tell you if romtool can correctly identify
your ROM.
The supported
field indicates whether romtool can find a map of the
ROM. A map is a set of files that tell romtool where to find things
like monster data, spell data, text, and the like. If supported
is
no, only very basic commands will work. Romtool ships with maps for
only a few games, so far, but it is possible to create your own.
If a map is found, the map
field will show where it is. This is useful
if you want to see how maps work, or to create your own.
romtool dump
extracts data tables from a ROM, and is usually the next
thing you'll want to try. The following example will find all known data
tables in 7thsaga.smc
, translate them into TSV files, and put those
files in mod_directory
:
user@host:~$ romtool dump 7thsaga.smc mod_directory
user@host:~$ ls mod_directory
armor.tsv dropsets.tsv monsters.tsv spells.tsv
characters.tsv items.tsv scripts.tsv weapons.tsv
In this example, the output files contain lists of the game's weapons, armor, monsters, items, etc.
The files thus created are plain-text files of tab-separated values. They can be opened in any spreadsheet program. I use visidata or LibreOffice.
WARNING: If possible, disable any auto-formatting or auto-correct features your spreadsheet program provides before proceeding. They will get in your way. See Useful Tools below for suggestions.
You can now edit the files created in the previous section. Change a monster's HP, or a weapon's attack value, or a name. Note that there are usually limits to what you can change. For example, if a monster's attack power is stored in the ROM as a single byte, you cannot give it >255 attack (the maximum single-byte value). If you're lucky, romtool will notice the problem and complain. Not so lucky, and it will cheerfully make a patch that breaks the game.
For the most part, as long as you limit any values you change to within the range of values used by the game, they should work.
WARNING: When saving your changes, be sure you save them in-place, in .tsv format, not .xls, .ods, .csv, or anything else.
When you're done making changes, do this to preview the patch:
user@host:~$ romtool build 7thsaga.smc mod_directory
PATCH
006263:0001:FF
006265:0001:FF
006272:0002:B2C2
0062FE:0008:01050E0C10221B15
006307:0006:21161C1E1D23
00630E:0000:000F:1
EOF
This prints out a textual representation of an IPS patch. If you are not familiar with the IPS format, it will look like gibberish, but that's okay. The main thing you want to check is the output's length; if you made a small change but get a huge patch, something is probably wrong.
If it looks okay, you can build the actual patch with:
user@host:~$ romtool build 7thsaga.smc mod_directory --out 7thsaga.ips
That creates an IPS patch named 7thsaga.ips
implementing your changes.
Give the patch the same name as the ROM, but with a .ips extension; this
will allow some (most?) emulators to automatically make use of it,
without physically modifying the original ROM.
Now you can fire up your emulator of choice and load the ROM. If the emulator supports implicit patching, and if your patch is named correctly, you should see your changes in-game.
Editing spreadsheets isn't the only way to generate a patch. Romtool also accepts changeset files -- YAML or JSON formatted descriptions of changes to make.
The main advantage of changeset files is self-documentation. You can tell at a glance what they will do. In contrast, it's easy to lose track of changes you've made to a spreadsheet. Also, changesets can be written in a standard text editor, so you don't have to fight with spreadsheet autoformat "features".
Their main disadvantage is the need to remember the names of object types and their properties. These are specified in the map files for each ROM. Also, you must understand YAML or JSON syntax to write changesets effectively.
Here is a YAML example:
# changeset.yaml
characters: # Top-level keys indicate the table to change
Esuna: # Second-level keys are the name or table-index of an object
hp: 200 # Third level are the object properties to change
mp: 200
spd: 50 # This will make Esuna considerably stronger out the gate
monsters:
Hermit:
hp: 500 # But so are the Hermit monsters found near the start
The preceding changeset can be supplied to romtool build
instead of a
directory. The resulting patch will do exactly what you expect:
user@host:~$ romtool build 7thsaga.smc changeset.yaml
PATCH
006263:0001:C8
006265:0001:C8
00626A:0001:32
0078DD:0002:F401
EOF
IPS patches are binary, thus annoying to inspect. You can convert them to a readable text format like this:
user@host:~$ romtool convert patch.ips patch.ipst
Romtool's .ipst
format is a textual representation of IPS. You can
read it in a standard text editor. If you are familiar with the IPS
format, you can also change it and then convert it back.
Here are some additional features that have not been added yet, but that I want to support. This isn't a complete list, just the things that come to mind at time of writing:
- ROM expansion
- Empty-space search (FIXME: Done but not documented)
- Game documentation generation (FIXME: Done but not meta-documented)
- monster/item/equipment lists (for players)
- data table specs (for romhackers)
- Alternate input and output formats
- More than just tsv for maps.
- More than just IPS for patches
- Archaeological tools
- working out text encodings
- locating strings
- locating data arrays
You can see the maps that ship with romtool here. Their completeness varies.
Note that you can get help at any time with romtool --help
or romtool {subcommand} --help
. Trust the help output more than this section,
because I may forget to update it from time to time.
You can add --verbose
to any command to show more information about
what is happening.
Notes on specific common issues follow.
romtool: command not found (or not recognized, etc)
The directory romtool was installed to isn't in your PATH. You'll have
to add it. On linux, this is probably $HOME/.local/bin
. On windows, it
is %LOCALAPPDATA%\Programs\Python\PythonXY\Scripts
.
Q. ROM map detection failed. Why?
Possible causes:
- There may not be an available map for your ROM. At the moment only a few games are supported out of the box. If there isn't an existing map, you will have to create your own. I haven't written documentation for this process yet, but looking at the contents of the data/maps directory in the repo will probably be informative.
- You may have an SNES ROM with an SMC header. The header changes the sha1 hash of the rom, which is what romtool uses to identify it. Remove the header and try again (eventually romtool will be able to do this for you).
- The ROM may have been physically modified, perhaps by applying a patch to the file instead of relying on an emulator's implicit patching. If you are trying to dump data from a modified ROM, you can specify a map with the -m option.
Q. My system doesn't know what program to use to open .tsv files.
The tsv file type may not be associated with anything. The method for associating filetypes differs by OS. On Windows 7 you can do it from the file properties; look for "Opens With <something>" followed by a button marked 'Change'. Other Windows versions should be pretty similar. On Linux you're on your own, but you probably Googled the answer before you got here anyway.
Q. The changes in my patch don't show up in-game.
-
Your patch may be named incorrectly. It should usually have the same filename as the ROM, but with a .ips extension.
-
Your emulator may not support implicit patching. Either physically apply the patch with
romtool apply
, or use an emulator that does support it. Here is a list of emulators known to support implicit patching:- ZSNES
- snes9x
- FCEUX (name as romname.nes.ips instead of romname.ips)
- FIXME: Add more here....
Q. My patch changes produce garbage.
Probably your spreadsheet application's autoformat function is trying to be smart. Turn it off.
Q. I already have a modified ROM and want to make a patch from it.
Do this:
romtool diff original.rom modified.rom -o patch.ips
Q. I have an IPS patch and want to see what's in it.
Do this:
romtool convert patch.ips patch.ipst
TODO
Notes on creating map files properly go here...
Notes: the various map spec files may have any number of extra columns not used by romtool. This is intentional; extensions or client applications can implement UI hints by looking for extra columns in the spec.
(there probably needs to be a convention for app-specific columns vs extension columns vs official columns...)
- For tsv editing:
- visidata is a vim-like terminal editor for tabular files. Use this if you’re already comfortable with vim.
- [LibreOffice Calc][lo]: A free spreadsheet editor. It works for the purpose, but it will take some coercion to make it stop helping you.
- Assemblers:
- Debugging:
- Mesen and Mesen-S are emulators with excellent built-in debuggers.
TODO: give romtool a way to take an asm file, pass it to fasm, and produce a patch inserting the result at a given offset.
TODO: detect and optionally download external tools. No installable packages for fasmg (yet) or the macro sets (probably ever).