The Binding of Isaac: Repentance · Android port

Isaac Port Modding

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

  1. ExportThe game's files as one zip: Mods → Export game files.
  2. EditChange the files you need. Keep their names and paths.
  3. PackA folder with mod.json and resources/ → zip.
  4. AddMods → + Add mod → pick the zip.

That is all a picture or sound mod needs. Past the route: mod.json in full, settings, changing tables with patch/, new enemies with content/, starting items, when the port does not use a table, what ADD MOD does with each file, and the example mod that shows all of it.

What a mod can and cannot do

Can: replace files, change tables

  • sprites of characters, items, monsters, bosses, rooms and the HUD (png)
  • animations (anm2)
  • sounds (wav) and music (ogg)
  • fonts (fnt and their png pages)
  • the game's texts in every language (stringtable.sta)
  • cutscenes (ogv, subtitles srt)
  • data files (xml, rooms stb) replaced whole
  • the values of the game's tables changed in place, and items added to pools, bosses to boss pools, keys to the texts (patch/)
  • new variants of enemies the game has (content/entities2.xml)
  • 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.

A mod may have three more folders at its top, each explained further on: patch/ (changes to the game's tables), content/ (new enemy variants) and options/ (the versions of the mod's files its settings pick).

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."
}
FieldWhat it is
nameThe name in the mods list. Required.
idThe 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, authorShown in one line under the name on the mod's page.
descriptionShown 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/.

FolderUsed 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

  1. Main menu → Mods → Export game files.
  2. 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.
  3. 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

  1. Make a mod folder. Put mod.json in it, and a resources/ folder with your changed files at their paths.
  2. 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.
  3. 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 pageWhat it does
SettingsOnly for a mod that declares them: one row per setting, as on the Tweaks page.
EnabledTurns the mod on or off without removing it.
Move up / Move downThe order. When two mods replace the same file, or change the same value of a table, the one higher up wins.
ExportPacks the mod back into a zip and saves it into a folder you pick.
DeletePress 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 ]
}
FieldWhat it takes
nameText, or text in languages. Required: a mod without a name is refused. The page shows up to 95 bytes of it.
idText: 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.
versionText, in quotes: "1.0". A bare number (1.0) is not shown on the page. Up to 31 bytes.
authorText, or text in languages. Up to 63 bytes.
descriptionText, or text in languages. Up to 511 bytes; a longer one is cut at a whole letter.
settingsA list of up to 16 settings (Settings).
anything elseIgnored, with a note in check.txt.

Texts in languages

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:

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 lineWhat 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 JSONTake out // and /* */.
broken [syntax]: … strings take double quotes, not single'text' → "text".
broken [syntax]: … the key "name" twice in one objectKeep one.
broken [syntax]: … a raw control character U+000A inside a stringA 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 mostShorten it, in every language.
refused [default_not_value], [default_type], [default_grid]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 4max 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.

KindOn the pageFolder read
switchON / OFFoptions/<key>/on/ or …/off/
choicethe chosen value's labeloptions/<key>/<value>/
numberthe numberof 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.

"settings": [
  {
    "key": "skin",
    "kind": "choice",
    "label": { "en": "Isaac's skin", "ru": "Кожа Айзека" },
    "values": [
      { "value": "usual", "label": { "en": "Usual", "ru": "Обычная" } },
      { "value": "green", "label": { "en": "Green", "ru": "Зелёная" } }
    ],
    "default": "usual"
  },
  {
    "key": "coins",
    "kind": "number",
    "label": { "en": "Starting coins", "ru": "Монеты на старте" },
    "min": 0, "max": 15, "step": 5,
    "default": 0
  },
  {
    "key": "onion",
    "kind": "switch",
    "label": { "en": "Sad Onion at start", "ru": "Грустный лук сразу" },
    "default": false
  }
]
FieldWhat it takes
keyRequired. a-z, 0-9, -, _, up to 32; one per setting. It is the folder's name under options/.
kindRequired. switch, choice or number.
labelRequired. Text or text in languages, up to 24 letters in each.
defaultRequired. A switch: true or false. A choice: one of its values. A number: a whole number on its steps.
valuesA choice's 2 to 16 values: {"value": "<folder>", "label": …}; the value follows the key's rules, its label up to 24 letters.
min, max, stepA 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.
restartOptional, true by default. See below.

How the folders lie

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>
WriteWhat 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

TableRecord: keyLists, children
players.xmlplayer: iditems (numbers); card, pill, trinket, pocketActive, costume must exist
items.xmlpassive/active/familiar, trinket, null: idcache, tags (words)
itempools.xmlPool: Name (31 pools)<Item Id>: add, change, remove
bosspools.xmlpool: name<boss id>: add, change
pocketitems.xmlcard/rune, pilleffect: id
challenges.xmlchallenge: idstartingitems, startingitems2, startingtrinkets (numbers); achievements, roomfilter (words)
entities2.xmlentity: id + variant + subtypetags (words); new records only through content/
costumes2.xmlcostume: type + id
stringtable.stacategory: name; key: namenew keys; <string> by language or position

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

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:

  1. The base: the whole file of the highest mod in MODS that brings one, else the game's.
  2. Completion: a base from an older game gets the records the game has and it lacks.
  3. content/ of every mod, highest first.
  4. 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 recordThe 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 poolBoth apply. No conflict.
both replace the whole fileThe 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 patchesThe 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:

<entities anm2root="gfx/" version="5">
    <entity anm2path="010.050_green gaper.anm2" baseHP="12" boss="0" champion="1"
            collisionDamage="1" collisionMass="5" collisionRadius="13" friction="1"
            id="10" name="Green Gaper" numGridCollisionPoints="12" shadowSize="14"
            stageHP="0" variant="50">
        <bestiary parent="10.1"/>
        <gibs amount="6" blood="1" bone="1" eye="1" gut="1" large="0"/>
    </entity>
</entities>

The bestiary: parent= or a page of its own

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:

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.

TableNumbers the game has room for
players.xmlplayer 0 to 40
items.xml, items_metadata.xmlitems 0 to 732, trinkets 0 to 189, null (and any unknown tag) 0 to 131
pocketitems.xmlcards and runes up to 97, pills up to 49
challenges.xml0 to 45, and the first challenge must have an id
costumes2.xmlby its type: as items, trinkets or nulls
wisps.xml, locusts.xmlitems 0 to 732; s0 to s18
sounds.xmlno negative id
cutscenes.xml, stages.xml, seedmenu.xml0 to 26, 0 to 36, 0 to 79
bossportraits.xmla boss with <alt>: 0 to 103
recipes.xmlinput in plain ASCII
entities2.xmlno 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.

What ADD MOD does with each file

Every file of the archive falls into one of three classes:

ClassFilesWhat happens
Read by the portmod.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 weighteverything else: .lua scripts, the rest of content/, READMEs, sources, other kinds of file in resources/, folders inside patch/, files in options/ outside a value folderPut 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"/>
  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.
  2. 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.
  3. 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.
  4. 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.
  5. 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:

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:

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.

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:

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 modIts files in the exportWhat you can change
Specialist Danceresources/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.xmlThe 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 Barsresources/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 themAny 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 wavesresources/gfx/port_wavebar.png and port_wavebar.anm2The wave bar's look.
Completion Marksresources/gfx/port_completion_widget.png and port_completion_widget_pause.pngThe marks widget on the HUD and on the pause screen.
Item Descriptionsthe 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 itThe 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

  1. Export the game's files (step 1) with Graphics ticked, and Music if you are after the dance's tracks.
  2. 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.
  3. 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.
  4. 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

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.

  1. 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/.
  2. 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/.)
  3. 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.
  4. 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.
  5. 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 seeWhat 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/… linesA 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 reasonThe 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)"Not an error: the files listed are carried along unused (what ADD MOD does with each file).
The mod was added, but nothing changedA 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 pageAnother 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: …"A whole table with a record the game has no room for. The game's file is used instead (when the port does not use a table).
A picture is broken, frames are cut wrongThe sheet's size or layout changed, but its anm2 did not.
The game crashes right after startA 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:

ApproachWhat it would mean
Run PC Lua mods as they areA 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 portA 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 portWhat 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.