The port takes resource mods: mods that replace the game's pictures, animations, sounds, music, fonts, texts and videos. A mod can also change the game's tables (item pools, characters, items) without replacing them, add new variants of enemies, and carry settings of its own. This guide shows how to make one from the game's own files, pack it and install it.
The whole route
ExportThe game's files as one zip: Mods → Export game files.
EditChange the files you need. Keep their names and paths.
settings on the mod's page that pick between versions of its files (options/)
its name, description and settings in several languages
Cannot: add behaviour or new kinds of things
Lua scripts (main.lua or any .lua): installed with the mod, never run
new items, trinkets, cards, pills, characters or challenges: the game keeps them in tables of a fixed size
the rest of content/ (everything but entities2.xml)
settings beyond a switch, a choice and a number that pick files, or new menu entries
anything that changes the rules of the game through code
This comes from the game itself. The port runs the code of the iOS version of Repentance, and that version was built without Lua and without a modding API: it has no script loader and none of the functions PC scripts call. So the port can do what its engine can: use a mod's file instead of the game's own. The table merging of patch/ and content/ is the port's own: it builds the finished table from the game's file and the mods' changes before the game reads it, so the game sees one ordinary file.
About xml and stb. A data file in resources/ replaces the game's whole. The port completes an older table with the records the game asks for and refuses one the game's loader would write past (when the port does not use a table), but a wrong value in items.xml, entities2.xml, players.xml or a room file can still break the game. To change a few values, use patch/ instead: it touches only what it names, and two mods can change one table together.
What a mod looks like
A mod is a zip archive. Inside there is a mod.json description file and resource folders laid out the same way as in a PC mod:
green-isaac.zip
├── mod.json required (or a PC mod's metadata.xml)
├── preview.png optional, kept with the mod
├── resources/ files for every language
│ └── gfx/characters/costumes/character_001_isaac.png
├── resources-dlc3/ optional, same as resources/
└── resources-dlc3.ru/ optional, Russian version only
└── gfx/ui/main menu/titlemenu.png
The path inside resources/ is the file's path in the game. The file resources/gfx/characters/costumes/character_001_isaac.png replaces Isaac's body. Letter case in paths does not matter.
All of this can sit at the top of the archive or inside a single folder; the port finds mod.json either way. A file the port does not read (a README, a script, source art) does not stop the mod: it is put into the mod's folder as it is and listed when the mod is added (what ADD MOD does with each file).
mod.json
{
"id": "green-isaac",
"name": "Green Isaac",
"version": "1.0",
"author": "Your name",
"description": "Isaac with green skin. Replaces one sprite."
}
Field
What it is
name
The name in the mods list. Required.
id
The mod's own mark: lowercase latin letters, digits, - and _, up to 40 characters. May be left out; it is then made from the name. The folder is named by the port, not by the id (4). When a zip has the same id, name and author as an installed mod, the port asks whether to update it or put it beside.
version, author
Shown in one line under the name on the mod's page.
description
Shown on the mod's page, wrapped to its width. Use \n for a line break.
The file is strict JSON, and names, descriptions and settings may come in several languages: see mod.json in full.
Language folders
For every file the game asks for, the port looks for a replacement in this order: the folder of the language the game is running in, then resources-dlc3/, then resources/.
Folder
Used when the game is in
resources-dlc3.ru/
Russian
resources-dlc3.de/, .es/
German, Spanish
resources-dlc3.jp/, .kr/, .zh/
Japanese, Korean, Chinese
resources/, resources-dlc3/
Any language, when the language folder does not have the file
English and French read the shared folders. The files the game keeps per language are mostly menu and HUD lettering; the export shows where each file lives.
1. Export the game's files
Main menu → Mods → Export game files.
Tick the kinds you want. Each shows its file count and size. The whole set is about a gigabyte, mostly music (~380 MB), sounds (~245 MB) and video (~145 MB). For re-colouring sprites, graphics alone is enough: about 200 MB.
Choose where to save isaac-repentance-files.zip and wait: the window counts the files as they go.
The zip has the same folders as a mod: resources/ and resources-dlc3.<language>/. Any file from it, put into a mod at the same path, replaces the game's one.
Take the files from here rather than from the PC version. The port runs on the iOS version, and its interface is its own: the touch buttons, menus and some lettering are drawn differently. A PC file works too when the same file exists here.
2. Edit
Pictures: png
Use any editor that keeps transparency and save as PNG with alpha, under the same name. Most pictures are frame sheets: an animation cuts its frames out by coordinates. Paint over them without moving anything or changing the sheet's size. If the sheet has to change, change its anm2 too.
Animations: anm2
These are XML: frame coordinates, timing, layers. The easiest editor is IsaacAnimationEditor, found in the PC game's folder: The Binding of Isaac Rebirth/tools/IsaacAnimationEditor. A text editor works as well. Do not delete or rename layers or animations: the game looks them up by name and number and crashes when one is missing.
Sounds and music
Sounds are WAV, 16-bit, 44100 Hz, mono, like the originals. Music is OGG Vorbis. Any length works.
Texts: stringtable.sta
An XML file with every string of the game in every language at once. A mod replaces it whole, so edit the exported file, not the PC version's table: their keys can differ.
Fonts: fnt
Binary BMFont files with png pages. The simplest change is to redraw the pages and leave the fnt alone.
Put only the files you changed into the mod. The game uses its own for everything else, so there is no need to copy the whole gigabyte.
3. Pack
Make a mod folder. Put mod.json in it, and a resources/ folder with your changed files at their paths.
Zip it. On Windows: select the folder's contents → right-click → Send to → Compressed (zipped) folder. On a phone: Compress or Archive in the file manager.
Check the archive: mod.json at the top (or inside one folder), resources/ next to it (and patch/, content/, options/ if the mod has them). Anything else would only be carried along unused.
4. Add and manage
Main menu → Mods → + Add mod → pick the zip. The port checks the archive and says what is wrong with it, if anything. If all is well, the mod goes first in the list, switched on, and the game restarts. Its folder gets a number and the mod's name (001-Reflashed), so two mods never take each other's place. Adding a mod that is installed already (the same id, name and author in mod.json) asks first: Update puts the new version into the mod's folder and keeps its place in the list and its on/off; Beside it adds it as a separate mod.
Your mods are listed under Your mods. The arrow next to a mod opens its page: the name, the version and author, the description, then the mod's Settings if it has any (settings), then its rows:
On a mod's page
What it does
Settings
Only for a mod that declares them: one row per setting, as on the Tweaks page.
Enabled
Turns the mod on or off without removing it.
Move up / Move down
The order. When two mods replace the same file, or change the same value of a table, the one higher up wins.
Export
Packs the mod back into a zip and saves it into a folder you pick.
Delete
Press twice. The mod leaves the list at once and its settings are forgotten; its folder is erased at the next start.
Under the rows, in small type, the page says what the port made of the mod when there is something to say: the tables it built from it, files it completed or did not use, pictures of the wrong size, settings mod.json switched off. Each such line ends with mods/<folder>.check.txt: that file, in the mods folder, has the details, line by line.
Changes take effect after the game restarts; a note at the bottom of the page reminds you. The game keeps what it has loaded in memory and does not re-read files on the fly.
The mods themselves live in Android/media/com.tboi.rebirth/mods/ - a folder any file manager can open, no Shizuku or special access needed (it moved there from Android/data; the game moves your files over by itself). The system file manager also shows it under TBOI: Rebirth. The same folder has mods.txt: the list of mods in order, with lines on 001-Reflashed and off 002-… (the folder's name).
mod.json in full
{
"id": "example-mod",
"name": { "en": "Example Mod", "ru": "Пример мода" },
"version": "1.0",
"author": "Isaac port guide",
"description": {
"en": "What a mod can do without a script.",
"ru": "Что умеет мод без скрипта."
},
"settings": [ … see Settings ]
}
Field
What it takes
name
Text, or text in languages. Required: a mod without a name is refused. The page shows up to 95 bytes of it.
id
Text: a-z, 0-9, -, _, starting with a letter or digit, up to 40 characters (capitals are made lowercase). Left out, it is made from the Latin letters and digits of the name; a name with none of them (all Cyrillic, say) needs an id.
version
Text, in quotes: "1.0". A bare number (1.0) is not shown on the page. Up to 31 bytes.
author
Text, or text in languages. Up to 63 bytes.
description
Text, or text in languages. Up to 511 bytes; a longer one is cut at a whole letter.
name, author, description and every label of a setting may be plain text or a map of languages: {"en": "…", "ru": "…"}. The codes are the port's language sets, in lowercase: en, ru, de, es, jp, kr, zh. The mod's page takes the text of the game's language, else en, else the first one given. In Japanese, Korean and Chinese the page shows en: the menu font has no letters for them. The windows of ADD MOD follow the phone's language the same way. The mod's folder and the "same mod?" question use the en text (or the first), so a phone in another language never turns one mod into two. A code the port does not know ("RU", "fr") gets a note and is shown only when nothing else is there.
The page draws these texts with the game's own menu font: Latin with the accents of French, German and Spanish, and Cyrillic. It has no long dashes (–, —), no «» and no Chinese, Japanese or Korean letters; a letter it lacks is simply not drawn.
Strict JSON
mod.json is read by the port's own reader, the same one at ADD MOD and at every start, and it takes JSON exactly as the standard has it:
UTF-8 (a BOM at the start is fine, Notepad writes one);
no comments, no comma after the last item, no single quotes, no leading zeros (05);
no key twice in one object;
no real line break or tab inside a string: write \n;
at most 64 KB, nested at most 32 deep.
Anything the reader calls broken (not JSON) or refused (a setting it would switch off) refuses the mod at ADD MOD, with its own lines, in English, naming the line and column; a note (an unknown field, an unknown language) only goes to check.txt. The same lines are written into mods/<folder>.check.txt at every start, so a mod.json edited by hand later shows its fault there and on the page: mod.json could not be read (syntax), or Settings turned off: 1 (key_bad) when only a setting is wrong (the rest of the mod works).
The line
What to do
broken [syntax]: … a comma before '}' (trailing commas are not JSON …)
Remove the comma after the last item of the list or object.
broken [syntax]: … comments are not JSON
Take out // and /* */.
broken [syntax]: … strings take double quotes, not single
'text' → "text".
broken [syntax]: … the key "name" twice in one object
Keep one.
broken [syntax]: … a raw control character U+000A inside a string
A line break typed inside a text: write \n.
refused [key_bad]: setting 1: key "Skin" must be 1 to 32 of a-z 0-9 _ -
Keys name folders: lowercase.
refused [label_long]: setting 3 "onion": the label: the "en" text is 32 characters long, 24 at most
The default must be one of the values, true/false for a switch, on the number's steps.
refused [num_grid]: … max 15 is not reached from min 0 in steps of 4
max must be min plus whole steps.
refused [kind_bad], [values_count]
kind is switch, choice or number; a choice has 2 to 16 values.
Settings
The port runs no scripts, so a mod is only files, and a setting can do one thing: choose which of the mod's files the game sees. Each value of a setting may have a folder, options/<key>/<value>/, laid over the mod's own files when the setting has that value. Inside it, the mod's layout again: resources/, resources-dlc3/, resources-dlc3.<language>/, content/, patch/. So a value can swap pictures, sounds, animations, texts, or change tables.
Kind
On the page
Folder read
switch
ON / OFF
options/<key>/on/ or …/off/
choice
the chosen value's label
options/<key>/<value>/
number
the number
of the folders named by a whole number, the largest not above the value
A value with no folder changes nothing: the game sees the mod's own files. So a switch usually has only on/, and a choice has a folder for every value but its default. A number's folders are steps: 0, 1, 2, 3 for one per value, or 0, 5, 8 on a scale of 0 to 10 for "few, some, many" without a copy for each value.
Required. a-z, 0-9, -, _, up to 32; one per setting. It is the folder's name under options/.
kind
Required. switch, choice or number.
label
Required. Text or text in languages, up to 24 letters in each.
default
Required. A switch: true or false. A choice: one of its values. A number: a whole number on its steps.
values
A choice's 2 to 16 values: {"value": "<folder>", "label": …}; the value follows the key's rules, its label up to 24 letters.
min, max, step
A number's: whole numbers within ±1 000 000, min below max, step above 0, at most 100 steps, max reached from min in whole steps.
restart
Optional, true by default. See below.
How the folders lie
The mod's own files first, then the settings' folders in the order the settings are declared: a later setting stands over an earlier one. Between mods the order in MODS decides, as always; a mod's settings live inside its place in the list.
A folder replaces and adds files; it cannot take a file of the mod away.
A folder's patch/ is applied on top of the mod's own patch/ and of earlier settings' patches: two settings of one mod may change one table, and that is never a conflict.
Every options/<key>/ must be a declared setting and every value folder one of its values (a switch has only on and off, a number only whole numbers), and a number needs at least one folder. Otherwise ADD MOD refuses the mod and names the folder: a misspelt folder would never come on, and nobody would know why.
Restart
The game reads its tables once and keeps loaded pictures and animations in memory, so a changed setting waits for a restart, as turning a mod on or off does: the page shows RESTART TO APPLY under the mod's rows. Set the value back to what the game started with and the line goes away. "restart": false, a promise that a setting takes effect without a restart, is accepted but not honoured yet: the port has not measured which files the game reads again, so it writes a note (restart_ignored) and still asks for the restart.
On the mod's page
The settings stand under a Settings heading, between the description and the mod's own rows (Enabled, Move up, Move down, Export, Delete). They work like the Tweaks page: a tap on a row selects it; a tap on the selected row flips a switch or moves a choice to its next value (round); a slide sideways steps the value, a number by its step, stopping at its ends; a pad or a keyboard steps with left and right. Labels are drawn as written, in the game's menu font, which sets lowercase as small capitals: write them like a sentence (Isaac's skin), since all capitals come out larger than the game's own rows.
mod-settings.ini
The values live in Android/media/com.tboi.rebirth/mods/mod-settings.ini, one section per mod (its folder), one line per setting that is not at its default:
[001-Example-Mod]
skin=green
coins=10
onion=on
A switch is on/off, a choice its value, a number its value, which must be on the steps. The file can be edited by hand while the game is closed; lines the port does not know (comments, other sections) are kept when it writes. A value the mod no longer has (after an update) counts as the default, the report says so, and the file is left alone until you change that mod's settings. Updating a mod keeps its folder and so its settings; Delete takes its section away.
Changing the game's tables: patch/
A whole data file in resources/replaces the game's. A file in patch/changes it: it names records by their key and says only what changes, so everything else stays the game's, and several mods can change one table at once. One file per table, at the top of patch/, named and rooted like the game's: patch/players.xml, patch/itempools.xml, patch/stringtable.sta.
<players>
<player id="0" items-add="1"/> add to a list
<player id="0" coins="10"/> set a value
<player id="3" unset="costume"/> remove an attribute
</players>
<ItemPools>
<Pool Name="treasure">
<Item Id="25" Weight="1" DecreaseBy="1" RemoveOn="0.1"/> new entry
<Item Id="1" Weight="0.5"/> change an entry
<Item Id="7" remove="true"/> remove an entry
</Pool>
</ItemPools>
Write
What it does
attr="v"
Sets the value (adds the attribute if the record has none).
attr-add="a,b"
Appends to a list. Lists of item numbers (items, startingitems) take repeats as given; lists of words (tags, cache) skip what is there.
attr-remove="a,b"
Takes values out of a list: the first of each from a list of numbers, every one from a list of words.
unset="a b"
Removes the attributes.
remove="true"
Removes the element: only pool entries (<Item>).
Within one element they apply in the order remove, unset, set, -remove, -add; several elements naming one record add up in the order of the file. The key attributes (id, Name, Id…) only select the record and are never changed; numbers compare as numbers ("07" is 7). Everything not named is copied byte for byte.
What can be appended, changed, or not at all
Changed: any attribute of any record the table keys, in every data table the game reads.
Appended: entries to item pools (<Item> in itempools.xml), bosses to boss pools (<boss> in bosspools.xml), keys to the texts (stringtable.sta), and new enemy variants through content/.
Removed: pool entries only.
Not at all: new records of the top level (a new item, character, card, challenge: the game's tables have fixed sizes), records the game keeps by position rather than by key (a sound's <sample>s, <color>s, <rule>s), a new category of texts, files the game never reads (menu_sounds.xml, translations.xml).
Table
Record: key
Lists, children
players.xml
player: id
items (numbers); card, pill, trinket, pocketActive, costume must exist
The other tables (music, sounds, stages, curses, recipes, wisps, babies…) take changes the same way, by their id. Texts:
<stringtable>
<category name="Default">
<key name="MY_KEY">
<string lang="English">Text</string> by language name (or id)
<string>Text</string> or by position, English first
</key>
</category>
</stringtable>
What a patch is refused
a record the table does not have: players.xml: patch …: player[77] refused - no such record (a patch changes records; new ones go through content/ where the table allows it);
a value naming an item, trinket, card, pill or costume the game does not have: that change is refused (Pool[treasure]/Item[9999] refused - Id 9999 is not a collectible of the game);
a pool name the game does not have, a new text category, a patch that does not read as XML, a table the port does not know;
an attribute the game's loader does not read is applied, with a warning line.
Replacing and changing together
Replacing and changing are different things. A mod whose resources/itempools.xml holds the whole file replaces every pool, as on PC; such mods keep working. A mod with patch/itempools.xml only adds and changes what it names. The port builds each table in this order:
The base: the whole file of the highest mod in MODS that brings one, else the game's.
Completion: a base from an older game gets the records the game has and it lacks.
content/ of every mod, highest first.
patch/ of every mod, lowest first, so the higher mod has the last word on every value. A patch lies on top of a replacement, wherever the replacing mod stands.
Two mods…
What happens and where it shows
patch the same value of the same record
The higher one wins. check.txt of both: players.xml: conflict player[0] coins: low and top - top wins; the page: Tables: patched 1; conflicts 1.
patch different values, or add to one pool
Both apply. No conflict.
both replace the whole file
The higher one is used; the other: not used - top replaces the file (the top of MODS wins), on its page not used 1.
one replaces, one patches
The patch lies over the replacement. If it names a record the game has but the replacement dropped: refused - overridden by legacy <mod>, on its page overridden by legacy <mod>: 1.
The page line reads Tables: replaced N, patched N, merged N; not used N; conflicts N; overridden by legacy <mod>: N - mods/<folder>.check.txt, with only the parts that are not zero; check.txt has a section Data tables built by the port… with every line that names the mod.
New enemies: content/
Of a PC mod's content/, the port reads content/entities2.xml: new variants of enemies the game has. Each <entity> takes a type the game has (id) and a variant (up to 4095) or subtype (up to 255) the game does not use, and is merged into the game's table after the game's own records:
Left out, with a line in check.txt: a record with the key of one the game (or a higher mod) has already, since that is a change, not a new variant (use patch/entities2.xml); a type the game does not have (a script's creature); a variant or subtype past the limits above. entities2.xml: content …: 1 new entities merged; 1 the game or a stronger mod has already, 1 of a type the game does not have (a script's creature) - left out.
Its anm2 and sheet go into the mod's resources/ as usual (resources/gfx/010.050_green gaper.anm2). An anm2root deeper than the game's (gfx/mymod/) is kept by prefixing the entity's anm2path.
The game puts an enemy where a room file names it. The port runs no script, so a new variant appears in play only through room files that place it.
The bestiary: parent= or a page of its own
<bestiary parent="10.1"/>: the variant counts as that enemy. It has no page of its own; its encounters and kills go to the parent's page (and to the achievements counted by kills of Uriel, Gabriel, Little Horn and Portal).
A <bestiary …> of its own: a page of its own at the end of its type, its own counters. While the mod is on, it is also one more enemy to meet for the achievement that asks for the whole bestiary. If that <bestiary> names an anm2path that neither the mod nor the game has, the port puts parent="<type>.0" in its place, with a line in check.txt.
The bestiary keeps counts in the save by type and variant. They stay when the mod is removed and do no harm, as the PC saves' extra entries do.
Parents that lead in a circle would hang the game: the table is then built again without that mod's content/.
Starting items
A character's starting set is items= in players.xml, and the game hands it out in full and unconditionally: every number, in order, locked items too. achievement= on a character locks the character in the menu; it does not touch the items. Change the set with a patch, <player id="0" items-add="1"/>, or take one out with items-remove.
On top of items=, the game adds a few things by code once they are unlocked (outside challenges): Isaac's D6, Eve's Razor Blade, Keeper's Wooden Nickel, Lazarus's Anemic, Magdalene's pill, and the trinkets of Cain, Samson and Keeper. Do not list D6, Razor Blade, Wooden Nickel or Anemic in items=: the game adds them anyway, and they come twice. Taking them away is code, not data. What data can do:
trinket= on Cain, Samson or Keeper replaces their coded trinket;
card= or pill= on Magdalene replaces her coded pill.
A number that is no item of the game (0, a hole such as 43, 61 or 235, past the last item, or negative in players.xml): in a patch the whole change is refused (player[0] refused - items 0 is not a collectible of the game). In a whole players.xml or challenges.xml a mod replaces, only that number is left out and the rest is kept, with a line in check.txt (legacy …: #MAGDALENE_NAME: items 9999 does not exist - left out) and on the mod's page (#MAGDALENE_NAME: item 9999 does not exist - left out).
When the port does not use a table
Many of the game's tables are arrays of a fixed size, and the game's loader writes each record into its slot without checking the number. A record past the end would be written over whatever lies after the array: a crash at start or, worse, a quiet corruption later. So before the game reads a mod's whole table, the port checks it against the loader's own limits, and a table with even one such record is not used: the next mod's whole file is taken, else the game's.
Table
Numbers the game has room for
players.xml
player 0 to 40
items.xml, items_metadata.xml
items 0 to 732, trinkets 0 to 189, null (and any unknown tag) 0 to 131
pocketitems.xml
cards and runes up to 97, pills up to 49
challenges.xml
0 to 45, and the first challenge must have an id
costumes2.xml
by its type: as items, trinkets or nulls
wisps.xml, locusts.xml
items 0 to 732; s0 to s18
sounds.xml
no negative id
cutscenes.xml, stages.xml, seedmenu.xml
0 to 26, 0 to 36, 0 to 79
bossportraits.xml
a boss with <alt>: 0 to 103
recipes.xml
input in plain ASCII
entities2.xml
no circle of <bestiary parent>
The page says Not used, the game would write past its table: players.xml - player id 41, the table holds 0..40, and check.txt: players.xml: NOT USED - player id 41 is not in the game's table 0..40 - …. This is why no table can grow: a 42nd character has nowhere to go.
A whole file that does not read as XML is not used either.
A record the game itself checks and skips (music 119, a sound above 832, a boss portrait 104 without <alt>) is no danger: the file is used and only a line is written.
The finished table (after content/ and patch/) is checked the same way. If it fails, it is built again without one mod's content/ and patch/, starting with the lowest mod in MODS, then without all of them, and at last the game's file is used. Each step is a line in check.txt.
An older whole table that lacks records of the current game gets them added (Files completed by the port): without them the game would crash where it asks for one.
What ADD MOD does with each file
Every file of the archive falls into one of three classes:
Class
Files
What happens
Read by the port
mod.json, metadata.xml, preview.png at the top; resources*/ files of the kinds the game reads (png, anm2, xml, wav, ogg, fnt, stb, sta, ogv, srt); content/entities2.xml; patch/<table>.xml and patch/stringtable.sta; the same inside options/<key>/<value>/
Unpacked and used.
Dead weight
everything else: .lua scripts, the rest of content/, READMEs, sources, other kinds of file in resources/, folders inside patch/, files in options/ outside a value folder
Put into the mod's folder as it is and listed in the window: Not used by the port (left in the mod's folder as it is). With a script, also: The mod's script does not run: the port runs no scripts, so part of what the mod does may be missing. Scripts are never run.
Junk
__MACOSX/, .DS_Store, Thumbs.db, .git/
Not unpacked.
The whole mod is refused only when it cannot be a mod or would break: neither mod.json nor metadata.xml; a mod.json the reader calls broken or refused (above); no name; a bad id; a path outside the mod's folder or with ..; an options/ folder mod.json does not declare, or a value its setting does not have; a number setting without a folder; more than a gigabyte unpacked; no file the port reads at all. The window names each reason, for example:
Mod "Example Mod" refused:
• options/skin/gray/ - choice "skin" has no value "gray" (it has: usual, green, grey, blue)
The example mod
example-mod.zip, beside this guide, shows everything above in nine files. Add it (Mods → + Add mod), restart, and open its page.
example-mod.zip
├── mod.json name and texts in en, ru, de, es; three settings
├── patch/
│ └── itempools.xml the seven meals join the treasure pool
└── options/
├── skin/ a choice: usual (no folder), green, grey, blue
│ ├── green/resources/gfx/characters/costumes/character_001_isaac.png
│ ├── grey/resources/gfx/characters/costumes/character_001_isaac.png
│ └── blue/resources/gfx/characters/costumes/character_001_isaac.png
├── coins/ a number 0..15 by 5: folders 5, 10, 15
│ ├── 5/patch/players.xml <player id="0" coins="5"/>
│ ├── 10/patch/players.xml
│ └── 15/patch/players.xml
└── onion/ a switch: only on/
└── on/patch/players.xml <player id="0" items-add="1"/>
Always on:patch/itempools.xml adds Lunch, Dinner, Dessert, Breakfast, A Snack, Midnight Snack and Supper to <Pool Name="treasure">. Nothing else of the 31 pools changes, and another mod's pool patch adds up with it.
Isaac's skin (a choice): Green, Grey and Blue lay the game's own sheets character_001_isaac_green.png and so on over character_001_isaac.png: the same size and frames, so nothing else needs to change. Usual has no folder: the game's own sheet.
Starting coins (a number, 0 to 15 by 5): each step's folder patches Isaac's coins. At 0 there is no folder, and Isaac starts with no coins, as in the game.
Sad Onion at start (a switch, off by default): on/ appends item 1 to Isaac's items. With Starting coins at 10 as well, two folders patch the same record, and Isaac gets coins="10" items="1", no conflict.
After a change the page shows RESTART TO APPLY; after the restart a new run shows it. mod-settings.ini gets a [<folder>] section with only what is not the default, and mods/<folder>.check.txt lists what the port built: itempools.xml: patch 001-Example-Mod: 7 changes, 0 refused, players.xml: patch 001-Example-Mod: 1 changes, 0 refused.
To make your own from it, change id and name first, so it is a mod of its own rather than an update of this one.
Bringing a mod over from PC
Steam Workshop mods live in The Binding of Isaac Rebirth/mods/<name>_<number>/. Look inside:
Only resources/ (maybe also resources-dlc3/ and metadata.xml): a resource mod. Zip the folder as it is and add it in the game: it is adapted once, as it is added (below).
There is a main.lua or a content/ folder: the mod changes behaviour or adds new things. Its pictures and sounds can still come: adapting keeps them and merges a content/entities2.xml (new enemies), and leaves the script and the rest of content/ out. Whatever the script did is missing, and for a mod built around its script that may be most of it (why). A mod that is only a script is refused (below).
Adapted at install
A mod without mod.json (a PC mod with its metadata.xml, or bare resources/) is adapted once, when you add it. ADD MOD runs the port's converter on the phone, the same rules as Isaac Mod Porter below, shows Adapting the PC mod: reading and …: writing, and installs what comes out. A mod with a mod.json is already in the port's format and is taken exactly as it is: nothing in it is resized, completed or left out.
What adapting does, file by file:
Pictures baked down. A picture the port draws at the game's layout that the mod drew at 2, 3 or 4 times the game's size is baked down to the game's size (pictures at the game's size). A size that is not a whole multiple is left out, and the game's picture stays.
Over 4096 halved. A picture wider or taller than 4096 pixels (the phones' limit) is halved, and every anm2 that cuts it is rescaled to match.
anm2 fitted. Every animation file is completed against the game's: layers, nulls and events it lacks are added empty, animations it lacks are cloned from its default one (an older mod). Two more fits, R0 and R1, are for pictures a mod redrew bigger (below).
Translations carried. An older items.xml, pocketitems.xml, entities2.xml and the like become the game's own file with the mod's name, description and hud texts carried in by id, so no item goes missing; a bare & or a repeated attribute is read the way the game reads it.
Left out, and named in the report: scripts, content/ other than entities2.xml, sources (psd, ase…), the folders of other games beside resources/ (resources-dlc4/ of Repentance+, resources-repentogon/), the five files the port reads by number (the menus the port adds to), room files of another version.
The window then says Adapted automatically - may work unstably, with a short count of what was done and where the report is. The mod's page in MODS starts its description with Adapted automatically from a PC mod - may work unstably. The converter's report, line by line (kept, dropped, rescaled, repaired, warning, note), is saved beside the mod's files: Android/media/com.tboi.rebirth/mods/<folder>/modport-report.txt.
"May work unstably" is meant literally. The converter judges every file by rules, not by eye. A mod made for Repentance+ (its menus and tables), or one whose pictures only its script asks for, can still show wrong art or nothing at all. When something looks wrong, the report usually names the file and why. To hand a mod to other players, adapt it once, check it, and share the result with its mod.json: then it is in the port's format and installs untouched.
Script mods are refused
When adapting leaves nothing the port reads (the mod is its main.lua, with no picture, sound or table beside it), nothing is installed, and the window says why:
Mod "Stats+" has nothing the port reads: it is a script mod,
and the port runs no scripts - there is nothing to install.
Without a script the reason is neither resources/ nor content/entities2.xml. A mod with a script and pictures that replace the game's is not refused: the pictures are installed, and the report says the script is not carried. Pictures of the mod's own, new to the game, are installed too, but only its script would draw them: such a mod installs and changes nothing on screen. The port has its own versions of several script mods, rewritten in C (how), Stats+, Planetarium Chance and the two Range Fix mods among them: turn those on in MODS instead.
R0 and R1: an anm2 for a picture drawn bigger
Some mods redraw a sheet at 2 or 4 times its size and change every anm2 that cuts it, so the crops are for the bigger picture. Two cases are not covered that way, and the converter fits them; its report line says repaired.
R0 - a file the mod forgot. An anm2 of the mod cuts a picture the mod ships at k times the game's size, but with the game's own crops: every crop inside the game's sheet, the frames drawn at about the game's scale. On PC that file shows the top-left 1/k of the art too. The converter multiplies its crops and pivots by k and divides the scale by k: the same size on the screen, the whole art. Report: …: its frames cut … with the game's crops - … fitted: crops times k, scale over k. In Reflashed this mends the seed widget, the character page swap, two pop-ups and the giant book's clouds. If you redraw a sheet bigger, change every anm2 that cuts it, and R0 never has to.
R1 - a file only the phone game has. The iOS game has animation files the PC one has not. The one its Repentance code uses is gfx/ui/extratouchwidgets.anm2: the seed widget of the character menu, drawn on touch instead of seedselectionwidget.anm2. A PC mod never brings it, so it would cut the mod's bigger pictures with the game's crops. When a mod ships a picture it cuts (seedselectionwidget.png) at k times the game's size, the converter puts the game's file into the mod with its frames fitted, as in R0. Report: …extratouchwidgets.anm2: the iOS game's own widget (no PC mod brings it) cuts …. Its paper, ExtraTouchWidgets.png, has no PC twin and stays the game's unless the mod draws one. A mod that brings its own extratouchwidgets.anm2 is left alone.
A PC mod with metadata.xml only
A PC mod needs no mod.json: when the archive has a metadata.xml (at the top or one folder down) and no mod.json, adapting (above) reads the card and writes a mod.json for the mod from it. The name comes from <name> (else the id), the version and description from theirs, and the id from <directory> made to the id's rules (lowercase, accents taken off, anything else becomes -), else from the name, else from the Workshop number. The card is read forgivingly, as PC cards are often not quite XML (a bare &). When both files are there, the one nearer the archive's top is the mod's card; on a tie, mod.json.
PC resource mods are drawn for the PC version. Sprites of characters, items, monsters and rooms are the same on iOS, but menu screens and the HUD can differ; compare those files with the export.
The menus the port adds to
The port's resources are the game's own files, untouched. What the port adds - its rows in the menus, the straps that open TWEAKS, MODS, GUIDE and the credits, the D-pad and the twin buttons of the touch controls - is laid over four of the game's animation files by name when the game starts:
gfx/ui/main menu/gamemenu.anm2
gfx/ui/main menu/optionsmenu.anm2
gfx/ui/pausescreen.anm2
gfx/ui/touch/ios_buttons.anm2
A mod may bring its own copy of any of them: the port lays the same additions over the mod's file, so the mod's menu keeps the port's rows and straps, and the stick sizes are made from the mod's ios_buttons.anm2 with the port's buttons added. A copy that does not read as an animation file is not used; the game's, with the port's additions, is. The port finds the game's layers it works with by their names in the file the game loads, so keep the game's layer, null and animation names (an older file is completed with the missing ones, below); where a name is still missing, the port leaves its change to that file out rather than draw on the wrong layer.
The pictures of these menus (gamemenu.png, optionsmenu.png, optionsextra.png, pausescreen.png, ios_buttons.png) are the mod's when it brings them. The port's own drawings live on sheets of their own (port_optionsextra.png, port_strap_*.png), and its few retouches of the game's sheets are used only when no mod brings that sheet: on a mod's gamemenu.png the credits strap meets the mod's picture as the mod drew it.
The port's own code still reads five files by layer and frame number. A mod's copy of them is not used, the game's is, and check.txt says so: gfx/ui/completion_widget.anm2, gfx/ui/hudstats2.anm2, gfx/ui/scoremenu.anm2, gfx/ui/buttons.anm2, gfx/ui/bosshp_icons/statuseffect_icon.anm2. Their pictures must therefore be the game's size (Pictures at the game's size).
Isaac Mod Porter (below) keeps a mod's four menu files when the sheets they cut are the game's size, and the port lays its additions over them. A menu drawn at twice or four times the size (Reflashed) comes with its own anm2 whose crops are for that size: the converter leaves that anm2 out, so the game's is used with the port's additions, and scales the pictures down to the game's size, along with the mod's other menus that share those sheets (splashes.png, sketch.png, seedwidget.png), so the menus keep the mod's look at the game's resolution. One more thing the phone game does by code: for the title it asks for titlemenu_ios.png after titlemenu.png; the converter puts the mod's picture under that name too, so a mod's title does not meet the game's sheet. The same goes for every sheet the game swaps in by code rather than through an anm2 (completion_widget_pause.png, grid_pit_corpse.png and the other pit floors, pickup_005_chests_coinslot.png): such a twin is made the size of the mod's base sheet, since it is drawn under the base's anm2. The touch editor and the Marked button show item and card pictures at the game's size: a mod that redraws them at twice or four times the size gets them scaled down by the converter; when the mod redrew the whole folder that way (all items, with its own pedestal anm2), the picture is kept for the game and the report says the port's button will show it too big. Everything else - sprites, backdrops, fonts, sounds, room files - is the same on both, and a mod's own sizes and layouts work as they do on PC.
Pictures at the game's size
A few of the game's pictures the port draws itself, by layer and frame number, and always through the game's own anm2, whatever a mod brings: the completion widget, the HUD stats (gfx/ui/hudstats2.png), the score card (gfx/ui/scoremenu.png), the pad glyphs (gfx/ui/buttons.png) and the boss bars' status icons. A mod's anm2 for these is not used, so their pictures must be the game's size: a repaint over the exported sheet, the frames where they are. A picture drawn at twice or four times the size (Reflashed's HUD stats are 4×) would be cut by the game's coordinates and show the wrong part of itself. The game does not scale pictures while it runs: it shows its own picture instead and says so on the mod's page in MODS ("Pictures of the wrong size") and in mods/<folder>.check.txt.
The converter does it for you. A sheet that is exactly the game's size times 2, 3 or 4 is baked down to the game's size: each 2×2 (3×3, 4×4) block becomes one pixel - the block's own colour when the art was blown up from single pixels, otherwise the average of the block, never a blur - and the report says rescaled … baked down. A size that is not a whole multiple cannot be baked; the picture is left out and the game's stays.
By hand. Open the exported picture to see its size and scale yours down by the same whole factor, with no smoothing: GIMP - Image → Scale Image, Interpolation: None; Aseprite - Sprite → Sprite Size, Nearest-neighbor; Photoshop - Image Size, Resample: Nearest Neighbor (hard edges). Bicubic and Lanczos blur pixel art and leave a fringe around transparent edges. Save it as PNG with alpha under the same name, and leave the mod's own anm2 for that file out.
An older mod: animations the game asks for
A mod made for an earlier version of the game can ship an anm2 that lacks an animation, a layer or a null the current game asks for by name and number (a door without GoldenKeyOpen, a character without SuperLeapUp), and that is a crash when the game reaches it. The same on PC. Compare such files with the export and add what is missing, or run the port's own converter (below), which does it for you.
The converter: Isaac Mod Porter
ADD MOD adapts a PC mod by itself (adapted at install); the same converter is also a web page, Isaac Mod Porter (modport.html), for reading the report before installing or making a mod to hand to others: open it in a browser on a phone or a PC, pick the mod (the Workshop folder zipped, or the folder itself on a PC) and press Convert. It runs entirely in the browser, uploads nothing, and gives back a zip the port takes, and apart from it a report of what it did, file by file (the report stays out of the zip: at a mod's root the port reads only its cards, and would carry the report along unused). It carries the game's own reference inside, so it needs nothing else: not Python, not the export. The page works from a link and also opened straight from the disk.
What it does, in this order: drops scripts, content/ and source files (fla, psd, pdn); leaves out the five files the port reads by number, and a menu anm2 whose sheets are not the game's size (the menus the port adds to); completes every anm2 against the game's (missing layers, nulls and events added empty, missing animations cloned from the mod's default one); leaves a picture of the mod's own size alone and notes it; bakes down the pictures the port draws at the game's size when they are a whole multiple of it (above); halves pictures over 4096 pixels (the phones' limit) and rescales the animations that cut them; merges an older items.xml, pocketitems.xml, entities2.xml and the like into the game's file by id, so nothing the game asks for goes missing; keeps room files whose version is the game's. Then it writes mod.json and resources/ into one zip, and the report beside it. Install that zip as any other mod: MODS → ADD MOD, or put it into Android/media/com.tboi.rebirth/mods/.
A translation mod goes through the same page. Its items.xml, pocketitems.xml, entities2.xml, babies.xml and the like are usually an older game's files with the names translated: the page keeps the game's own file and carries the translated name, description and hud texts into it by id, so no item or enemy goes missing. The game's own xml reader forgives a bare &, a < inside a value and a repeated attribute (translations that remap glyphs in their fonts lean on that); the page reads such files the same way and writes them escaped. A picture that is the game's width but shorter (an older game's controls.png) is left out, because the game's crops would reach past it; a picture with the mod's own fnt or anm2 is the mod's layout and is kept whatever its size.
The page speaks English, Russian, Spanish and Portuguese (the switch at the top; it follows the browser's language first). A big mod (Reflashed is 265 MB zipped) takes about twenty seconds on a PC and needs a browser with memory to spare on a phone; if the phone's browser gives up, run the page on a PC and copy the zip over.
Changing the built-in mods
The mods that come with the port (MODS in the main menu) are made of files like the game's, and a mod of yours replaces them the same way: the same file, at the same path, in your resources/. Nothing else is needed. The export has them next to the game's files when Graphics, Data or Music are ticked; always start from the exported file, so the size, the frames and the names stay what the port expects.
Where their files are
Built-in mod
Its files in the export
What you can change
Specialist Dance
resources/gfx/port_specialist/: one specialist_<character>.anm2 per character (isaac, magdalene, cain, judas, dark_judas, xxx, eve, samson, azazel, lazarus, lazarus2, eden, lost, lilith, keeper, apollyon, forgor_bone, forgor_soul, bethany, jacob, esau; the tainted: t_isaac, t_magdalene, t_cain, t_judas, t_bluebaby, t_eve, t_samson, t_azazel, t_lazarus, t_lazarus2, t_eden, t_lost, t_lilith, t_keeper, t_apollyon, t_forgor_bone, t_forgor_soul, t_bethany, t_jacob, t_jacob2), its sheet in sheets/ (the tainted in sheets_tainted/; the anm2 names the file), the dancing item in items/, the four tracks arcadespecalist.ogg (the regular characters), arcadespecalist_b.ogg (the tainted), crackhousespecialist.ogg (Tainted Cain) and arcadespecalist_eden.ogg (Tainted Eden), and costumes.xml and music.xml
The dance of any character: repaint its sheet, or redraw its anm2 with the same animation names. The music: put your own .ogg under the same file names.
Boss Bars
resources/gfx/ui/bosshp_bars/: one png per bar style, bossbar_design_<name>.png. resources/gfx/ui/bosshp_icons/: one png per boss face, and bosshp_icon_32px.anm2 that cuts them
Any bar style, any boss face. The list of styles and which face a boss wears are fixed in the port: repaint the files, do not add or rename them.
Boss Rush waves
resources/gfx/port_wavebar.png and port_wavebar.anm2
The wave bar's look.
Completion Marks
resources/gfx/port_completion_widget.png and port_completion_widget_pause.png
The marks widget on the HUD and on the pause screen.
Item Descriptions
the icons: resources/gfx/eid_inline_icons, eid_transform_icons, eid_cardspills, eid_player_icons (.anm2 and .png each); the fonts: resources/font/eid_default.fnt and its pages, and the Korean, Chinese and Vietnamese faces beside it
The icons inside the descriptions and the face they are written in. The texts themselves are not files: they live in the port's own database and are not changed by a mod.
Planetarium Chance and Stats+ draw nothing of their own and have no files to replace.
Step by step
Export the game's files (step 1) with Graphics ticked, and Music if you are after the dance's tracks.
Take the file you want from the paths above and edit it: the same size, the frames in the same places. For a png that an anm2 cuts, paint over the frames without moving them; if you must move them, edit the anm2 too, keeping every animation and layer and their names.
Put the edited file into your mod at the same path under resources/, with a mod.json beside it, zip it and add it in the game (3, 4). Only the files you changed go in.
Restart the game. The built-in mod draws your art, its switch and its settings on its page in MODS working as before.
What must stay
Names. The port asks for animations and layers by name: a character's dance, a bar style, a boss face, an icon. A name missing from your anm2 crashes the game, exactly as with the game's own files.
Specialist Dance's xml. In costumes.xml and music.xml keep the entries, their order and their names: the port counts the costumes and the tracks in that order. Change only the art and the tracks they point to.
Boss Bars' files. The styles and the faces are a fixed list in the port. A new file with a new name is never asked for, and a renamed one leaves a hole.
Item Descriptions' fonts. Keep every letter the descriptions use in every language you play in; a letter missing from the font is simply not drawn.
Settings do not clash. Your mod only swaps files; the built-in mod keeps its switch and its settings on its own page in MODS, and they work on your art as they did on the original. Turned off there, the built-in mod is not drawn, with your art or without. Your mod is on or off on its own row under YOUR MODS.
A new language, in place of an old one
The list of languages is fixed inside the game, so a language cannot be added. It can take the place of one the game has: German, Spanish, Russian, Japanese, Korean or Chinese. Everything it needs is a resource mod.
Export the game's files with Graphics and Data ticked (step 1). You need resources/stringtable.sta, the donor language's folder, for German resources-dlc3.de/gfx/ui/, and resources/font/.
Texts.stringtable.sta is XML: 3000 keys, and under each key one <string> per language in the order the file's header lists them. Rewrite the donor's strings and leave the order alone. The file is replaced whole, so two language mods cannot be used together. (A mod that changes only some strings can use patch/stringtable.sta instead, naming each key and <string lang="German">; such patches add up across mods: patch/.)
Lettering. The menus and the HUD are pictures. Repaint the donor's pictures that carry text, in its folder (resources-dlc3.de/gfx/ui/ has about nine hundred, most without text), and put only the repainted ones in the mod. The language's name on the LANGUAGE row is one of these pictures.
Letters. The game's fonts in resources/font/ hold Latin and Cyrillic. A language with letters beyond those (Ukrainian ї є ґ, Polish, Turkish) needs them drawn into the fnt and its png page; BMFont editors do that. Latin and Cyrillic languages skip this step.
Pack and use.mod.json, resources/ and the donor's folder, zipped as any mod. In the game pick the donor language, and the mod's words appear in its place.
The port's own pages (Tweaks, Mods, Guide, the item spawner) are lettered per language inside the app and stay in the donor's language. Item descriptions are a separate thing: they come in twenty languages and are chosen in MODS → Item Descriptions.
When something goes wrong
What you see
What it means
"The archive has neither mod.json nor metadata.xml, so it is not a mod."
Put mod.json at the top of the archive or into its single folder (a PC mod's metadata.xml will do too).
"The archive holds a script but neither mod.json nor metadata.xml…"
A script with nothing to describe the mod. The port runs no scripts; a PC resource mod is taken with its metadata.xml.
"mod.json will not do:" and lines broken […] / refused […]
The file is not strict JSON, or a setting is wrong. The line names the place: see mod.json in full.
"Mod "…" refused:" and options/… lines
A folder under options/ that no setting declares, a value the setting does not have, or a number with no folder (Settings).
"…has no file the port reads (resources/, content/entities2.xml, patch/, options/)."
Everything in the archive is dead weight: wrong folders, or kinds of file the game does not read (jpg, mp3). Use png, wav, ogg and so on, at the game's paths.
"…has nothing the port reads: it is a script mod, and the port runs no scripts - there is nothing to install."
A PC mod that is only its script. Nothing is installed (script mods are refused); if the port has its own version of it, turn that on in MODS.
"Adapted automatically - may work unstably"
Not an error: a PC mod was adapted as it was added (adapted at install). If something looks wrong, read mods/<folder>/modport-report.txt.
"The PC mod could not be adapted: …"
The converter stopped on the archive (a broken zip, or a mod too big for the phone's memory). Convert it with Isaac Mod Porter on a PC and add the zip it gives.
"The PC mod was adapted but not installed:" and a reason
The adapted mod failed one of the checks every mod goes through; the reason below that line is the same as for any mod in this table.
"Not used by the port (left in the mod's folder as it is)"
A path or file name does not match the game's (compare with the export), or another mod higher in the list replaces the same file, or the game was not restarted after the change. For a table, read the mod's check.txt: a refused patch line says why.
"Tables: …; conflicts N" on the mod's page
Another mod changes the same value; the higher one wins (patch/). Move the one you want up.
"Not used, the game would write past its table: …"
The sheet's size or layout changed, but its anm2 did not.
The game crashes right after start
A broken xml, anm2 or room file. You cannot reach the menu, so turn the mod off by hand: in mods/mods.txt change on id to off id, or delete the mod's folder.
Why scripts are not supported
In short: the game the port runs has nothing to run them with. Teaching it would not be hard, but keeping it working would be a job of its own, and I am not taking that job on.
The iOS version has no mod support at all
The port runs the iOS build of Repentance, and that build was compiled without Lua and without a mod loader. Its code has no Lua interpreter, none of the functions PC mods call (RegisterMod, AddCallback, Isaac.GetPlayer…) and nothing that merges a mod's content/ into the game's tables. What is left is the game reading its own files, and that is what resource mods use.
How the built-in mods got here
Item Descriptions, Stats+, Boss Bars, the completion marks and the Planetarium, Specialist Dance, Range Fix and Boss Rush tweaks are PC mods too. None of their Lua runs here. Each of them was rewritten in C inside the port: the game's code was disassembled to find where it keeps the player, the room and the items, and every feature was rebuilt on top of that, with the mod's Lua as the specification. Using AI, that part was quick.
So why not more
The hard part is not the rewrite, it is what comes after. Every mod brought in becomes a piece of the port that has to keep working: its bugs get reported, its settings need a page, its texts need seven languages, and every change to the port has to be checked against it. A general way to load mods would be bigger still:
Approach
What it would mean
Run PC Lua mods as they are
A Lua interpreter in the port and the whole Isaac Lua API rebuilt on top of the iOS version: hundreds of classes and functions. Writing them is doable; checking, fixing and supporting all of them for every mod people try is the size of REPENTOGON, the PC's own extension of the API: thousands of hours of work by over a dozen modders, and still maintained.
Native mods: compiled code loaded by the port
A loader, a programming interface and a published map of the game's code, which modders would keep extending, and which would have to stay stable and documented from then on.
Rewrite one particular mod into the port
What was done for the built-in ones. Quick to do, but each one is then the port's to maintain.
I am not planning any of these. I set out to make a port, and I made it. A full-scale modding platform, a separate project as big as the port itself, was never intended, and it never will be.
Content mods
A content/ folder adds entries to the game's tables: new items, entities, characters, challenges. On PC the mod loader merges them in and Lua gives them their effects. Here the port does the merging itself, as far as the game's tables allow: new variants of existing enemies from content/entities2.xml (new enemies), and changes and pool entries from patch/ (changing tables). New items, characters, cards and challenges stay out: their tables have a fixed size in the game's code, and an effect would need the script that is not there.