# Player mods (RomFS) [English](#english) | [中文](#chinese) ## English The Mac app loads **RomFS mods only**. `exefs/` files are not loaded (the runtime uses the rewritten `main`, `sdk` and `subsdk0`, and IPS or pchtxt files are not picked up automatically), and the app does not scan folders for mods. Whether a mod is used is decided by one file: `manifest.tsv`. The launcher points `TOTK_ROMFS_MODS` at ```text ~/Library/Application Support/MacNativeGames/TOTK/mods/ ``` where `` is the game version the app was built from: `v0` (1.0.0), `v112` (1.1.2), `v121` (1.2.1) or `v143` (1.4.3). ### What `manifest.tsv` is Only the files listed in `manifest.tsv` are served. Each line has four tab-separated fields: ```text ``` At startup the loader checks each listed file's size and SHA-256, and checks the original's SHA-256 against the game image. `-` in the last field adds a file the game's RomFS does not have (used by mods that add assets). Paths are case-sensitive and keep the RomFS spelling. A folder without `manifest.tsv` means "no mod". Files copied into the folder but not listed are not used. In detail: - A **new path** that is not listed is treated as absent: the game cannot see the file. - A **replacement whose size or hash changed** without regenerating the manifest stops the start with `RomFS overlay file missing or wrong size` or `RomFS overlay integrity mismatch`. - A path that exists in the game but is not listed is read from the game, so the copied file has no effect. ### Install a mod 1. Quit the game completely. 2. Copy the mod's RomFS paths **directly** into the version folder. If the mod ships a `romfs/` folder, copy its contents, not the folder itself: ```bash MODDIR="$HOME/Library/Application Support/MacNativeGames/TOTK/mods/v121" rsync -a --exclude='.DS_Store' --exclude='manifest.tsv' "/path/to/mod/romfs/" "$MODDIR/" ``` 3. Generate the manifest with the app you built. Run this from the repository root: ```bash python3 games/totk/scripts/make_mod_manifest.py "$MODDIR" \ --app "/path/to/Tears of the Kingdom.app" --version v121 ``` The script reads `Contents/Resources/totk/extracted//romfs` from the app, hashes each file and its original, leaves out files that are identical to the original, and writes `manifest.tsv`. It prints a line such as `...: 12 files (10 replaced, 2 added)`. 4. Start the game and check the log: ```bash grep "mods/v121 checked" "$HOME/Library/Logs/MacNativeGames/totk.log" | tail -1 ``` It should say `.../mods/v121 checked, N files`; additions also print `M files added to the RomFS`. ### Switching versions The game reads the installed folder as it is; it does not follow the folder you downloaded or unpacked. If the new version removes files, set the old version folder aside (or delete it after a backup) before copying, or the removed files stay active. Generate the manifest again after every change. ### Breath of the Wild The Breath of the Wild app reads its mods from ```text ~/Library/Application Support/MacNativeGames/BotW/mods/botw160 ``` Everything above applies: copy the mod's RomFS contents into that folder and generate the manifest with the app, naming the folder `botw160` or passing `--version botw160`: ```bash MODDIR="$HOME/Library/Application Support/MacNativeGames/BotW/mods/botw160" python3 games/totk/scripts/make_mod_manifest.py "$MODDIR" --app "/path/to/Breath of the Wild.app" ``` Check `grep "mods/botw160 checked" ~/Library/Logs/MacNativeGames/botw.log | tail -1` after the start. - A mod that makes a resource larger than the original also has to ship the matching resource size table, `System/Resource/ResourceSizeTable.product.srsizetable`, built for 1.6.0. Without it the game reserves the original size and stops at start (for example a translation whose `Pack/Bootup_.pack` grew). - The game's language follows the app's preferred language, which also picks the language folder a mod must use (`USen` for `en-US`). To give only this game another language: `defaults write org.macnativegames.botw AppleLanguages -array en-US`. ### Notes - Do not edit `manifest.tsv` by hand; regenerate it. - Do not change files while the game runs. The loader records each file's identity at start and stops if a file changes mid-session. - There is one overlay directory for player mods: to use several mods, merge their files into one version folder and generate one manifest. Do not nest mod folders. If two mods change the same path, choose one file. - The build replaces a few RomFS files itself: `UI/LayoutArchive/Common.Product..Nin_NX_NVN.blarc.zs` (quick-menu timing; `` is 100, 110 or 140 depending on the version), `Event/EventFlow/DmF_SY_GameOver.bfevfl.zs`, and with the 2K output `System/HeapSize/Product.Nin_NX_NVN.HeapDef.byml.zs`. A mod that replaces one of them stops the start with "two overlays replace the same file"; leave that file out of the mod for now (UI translation mods hit this, #73). - `exefs/` is not loaded; only RomFS content works. (`TOTK_PCHTXT` is a developer experiment for pchtxt patches, not a general mod loader.) - This guide covers the Mac app; installing mods into the iPhone/iPad app container is not covered. ## 中文 Mac App 只加载 **RomFS mod**。`exefs/` 里的文件不会被加载(运行时用的是改写后的 `main`、`sdk` 和 `subsdk0`,IPS 和 pchtxt 不会自动生效),App 也不会扫描文件夹找 mod。是否使用某个 mod,完全由 `manifest.tsv` 决定。 启动器把 `TOTK_ROMFS_MODS` 指向 ```text ~/Library/Application Support/MacNativeGames/TOTK/mods/ ``` `` 是构建这个 App 时使用的游戏版本:`v0`(1.0.0)、`v112`(1.1.2)、`v121`(1.2.1)、 `v143`(1.4.3)。 ### manifest.tsv 是什么 只有列在 `manifest.tsv` 里的文件才会被提供给游戏。每一行是四个用制表符分开的字段: ```text <大小> <原版文件的 SHA-256,或 -> ``` 启动时加载器会逐个核对 mod 文件的大小和 SHA-256,并核对原版文件的 SHA-256。最后一个字段是 `-` 表示这是游戏 RomFS 里没有的新文件(用于新增资源)。路径区分大小写,必须和 RomFS 里的写法一致。 没有 `manifest.tsv` 就等于没有 mod。只把文件复制进目录但不写进清单,游戏不会使用: - 清单里没有的**新路径**会被当成不存在,游戏完全看不到这个文件。 - 覆盖已有文件但大小或哈希变了、又没有重新生成清单,启动会直接报 `RomFS overlay file missing or wrong size` 或 `RomFS overlay integrity mismatch`。 - 原版有这个路径、但清单里没列,游戏会读取原版文件,复制进去的文件看起来“没有效果”。 ### 安装步骤 1. 先完全退出游戏。 2. 把 mod 的 RomFS 路径**直接**放进版本目录。如果 mod 带一层 `romfs/` 文件夹,复制的是它里面的 内容,不是 `romfs` 这一层: ```bash MODDIR="$HOME/Library/Application Support/MacNativeGames/TOTK/mods/v121" rsync -a --exclude='.DS_Store' --exclude='manifest.tsv' "/path/to/mod/romfs/" "$MODDIR/" ``` 3. 用你构建好的 App 生成清单。在仓库根目录执行: ```bash python3 games/totk/scripts/make_mod_manifest.py "$MODDIR" \ --app "/path/to/Tears of the Kingdom.app" --version v121 ``` 脚本会读取 App 里的 `Contents/Resources/totk/extracted//romfs`,计算每个文件和原版 文件的哈希,跳过与原始完全相同的文件,然后写入 `manifest.tsv`。成功时会打印类似 `...: 12 files (10 replaced, 2 added)` 的一行。 4. 启动游戏,确认日志: ```bash grep "mods/v121 checked" "$HOME/Library/Logs/MacNativeGames/totk.log" | tail -1 ``` 应出现 `.../mods/v121 checked, N files`;有新增文件时还会打印 `M files added to the RomFS`。 ### 换版本时 游戏读取的是安装目录本身,不会跟着你下载或解压的那个文件夹走。如果新版本删掉了旧版本的某些 文件,先把旧的版本目录整个移走(或备份后删除)再复制,否则被删掉的文件会继续生效。每次改动后 都要重新生成清单。 ### 旷野之息 旷野之息 App 从这里读取 mod: ```text ~/Library/Application Support/MacNativeGames/BotW/mods/botw160 ``` 上面的说明都适用:把 mod 的 RomFS 内容复制进这个目录,再用 App 生成清单(目录名为 `botw160`,或加 `--version botw160`): ```bash MODDIR="$HOME/Library/Application Support/MacNativeGames/BotW/mods/botw160" python3 games/totk/scripts/make_mod_manifest.py "$MODDIR" --app "/path/to/Breath of the Wild.app" ``` 启动后用 `grep "mods/botw160 checked" ~/Library/Logs/MacNativeGames/botw.log | tail -1` 检查。 - 如果 mod 让某个资源比原版大,还必须带上为 1.6.0 生成的资源大小表 `System/Resource/ResourceSizeTable.product.srsizetable`。没有它游戏会按原版大小分配,启动时就会停止(例如 `Pack/Bootup_<语言>.pack` 变大的翻译 mod)。 - 游戏语言跟随 App 的首选语言,也由它决定 mod 要放在哪个语言目录(`en-US` 对应 `USen`)。只给这个游戏换语言: `defaults write org.macnativegames.botw AppleLanguages -array en-US`。 ### 注意 - 不要手改 `manifest.tsv`,每次重新生成。 - 游戏运行中不要改 mod 文件;加载器会在启动时记录每个文件的身份,运行中变化会停止游戏。 - 玩家 mod 只有一个覆盖目录:多个 mod 就合并到同一个版本目录,最后生成一份清单;不要把 mod 文件夹嵌套进去。两个 mod 改同一个路径时,只能选一个文件。 - 构建本身也会替换几个 RomFS 文件:`UI/LayoutArchive/Common.Product..Nin_NX_NVN.blarc.zs`(快捷菜单时机,`` 按版本是 100、110 或 140)、`Event/EventFlow/DmF_SY_GameOver.bfevfl.zs`,开 2K 输出时还有 `System/HeapSize/Product.Nin_NX_NVN.HeapDef.byml.zs`。模组如果也替换其中一个,启动时会报“two overlays replace the same file”并停止,目前只能把那个文件从模组里去掉(界面翻译模组会碰到,见 #73)。 - `exefs/` 不会被加载,只有 RomFS 内容生效。(`TOTK_PCHTXT` 是开发者的 pchtxt 实验入口,不是 通用的 mod 加载器。) - 本文面向 Mac App;iPhone/iPad 的 App 容器内安装未在此说明。