TOTK BFRES Model Files
Vanilla RomFS Rule
Use a vanilla TOTK romfs path already supplied in the current turn or registered project guidance. Otherwise ask the user for it before inspecting game files. Treat the path as read-only; do not search unrelated folders or guess a location.
Use this skill for low-level Tears of the Kingdom model-file work: BFRES inspection, internal name edits, skeleton/vertex edits, and MCPK compression. It is a mechanics skill, not a task recipe.
For texture container replacement, DDS/ASTC packaging, BNTX/TXTG validation, or non-model texture work, use totk-texture-pipeline. Use this BFRES skill only for model/material context, texture-name bindings, BFRES tables, skeletons, vertices, and resource identity.
Related TOTK Skills
- Use
totk-toolkit for shared reusable BFRES/MeshCodec and local inspection scripts before creating one-off tools.
- Use
totk-texture-pipeline for BNTX/TXTG, DDS/ASTC, Switch Toolbox texture replacement, texture containers, and texture validation.
- Use
totk-local-operator habits for compact inventories, offset summaries, hashes, and validation logs instead of loading binary dumps into context.
Core Rules
- Treat source BFRES and vanilla/reference files as read-only. Write outputs to the requested working/output folder.
- When model outputs will be installed in a mod, ask whether the user wants additive unique paths, vanilla-path replacement, or to supersede another mod's path. Model path/name choices are often priority-sensitive and should follow the user's intent rather than defaulting to uniqueness.
- Prefer parsing the BFRES structure over hardcoded offsets. The weapon files studied here share offsets, but future files may not.
- Never patch counts casually. A Switch-Toolbox crash in
MaterialParserV10.LoadStrings was caused by reducing FMAT count while a retained shape still referenced material index 1.
- If output filename/model identity changes, update internal names too. At minimum repoint:
- BFRES header full resource name pointer at
0x20.
- FMDL model name pointer at
FMDL + 0x08.
- FMDL model dictionary entry pointing to the same base name.
- If the new name is longer than the old string slot, append a new BFRES string and repoint references. Do not overwrite adjacent string table data.
- For final
.bfres.mc, use real MCPK wrapping: MCPK header plus magicless ZSTD payload. Do not merely rename raw BFRES to .bfres.mc.
- For edited TOTK weapon models, do not trust a hand-patched expanded BFRES that merely opens in Switch Toolbox. The game may reject it. Normalize the edited raw BFRES through Switch Toolbox's BFRES save path, then MeshCodec-compress the saved compact BFRES to
.bfres.mc.
- For any geometry-affecting edit, recenter serialized
FSHP render bounds after the Toolbox save and before MeshCodec compression. Visible vertices and invisible render bounds are separate; stale boxes/spheres can make a correct mesh cull or disappear in camera/frustum tests. Use totk-toolkit/scripts/toolbox_recenter_bfres_render_bounds.ps1 or a pipeline that calls it.
- If a model appears only after camera rotation or disappears at a sharp preview/frustum threshold, inspect the final BFRES render bounds before chasing actor AABB, physics, or material flags. Tested inventory-preview culling did not respond to ActorInfo-style AABB overlays, but was fixed by recentering serialized
FSHP boxes/spheres on the edited geometry.
- For geometry edits, prefer Toolbox object-model edits over byte-stream surgery: load the expanded BFRES through
FirstPlugin.BFRES, edit the selected FSHP.vertices, call FSHP.SaveVertexBuffer(), save through Toolbox, recenter serialized render bounds, then MeshCodec-compress.
- Binding-looking failures can come from model-layer state, not only actor
AttachmentParam or RSDB bind-bone fields. If a fused model aligns on a bare hilt but misbinds on an intact weapon where Bind_A/Bind_B/Bind_C/Bind_W are separated from Bind_Z, inspect the final merged model BFRES before changing rotations. Compare against a working peer for Shape.VertexSkinCount, Shape.SkinBoneIndices, VertexBuffer.VertexSkinCount, VertexBuffer.Attributes such as _i0/_w0, StrideArray, VertexBufferSizeArray, and Buffers. Treat skinning or extra skin-index attributes as an investigation axis, not a proven standalone fix; one validated case showed that removing _i0 and clearing skin counts changed the BFRES correctly but did not change the in-game bind behavior.
- Treat weapon visuals as state-specific evidence. Loose-world item view, inventory fused preview, wielded fused world view, and dropped/loose fused weapon view can each apply different render, hide-material, attachment, animation, or culling paths. A model that looks correct in one state does not prove the BFRES, bind policy, or rotation is correct in the others; record which state was tested before changing geometry or rotations.
- TKVSC-derived tooling does not replace this BFRES/MCPK workflow yet. Its public beta surface is useful for archive browsing and BNTX/TXTG texture preview, but
.bfres and .mc support were still unfinished when reviewed. Use TKVSC-derived code only as read-only reconnaissance around models until it proves MeshCodec-equivalent expanded BFRES output.
Observed Switch BFRES FMDL Layout
For the TOTK weapon BFRES files validated in this project:
FMDL + 0x20 = FVTX array pointer
FMDL + 0x28 = FSHP array pointer
FMDL + 0x30 = FSHP dictionary pointer
FMDL + 0x38 = FMAT array pointer
FMDL + 0x40 = FMAT dictionary pointer
FMDL + 0x68 = FVTX count
FMDL + 0x6a = FSHP count
FMDL + 0x6c = FMAT count
When selecting one visible shape without rebuilding all tables, patch only the FSHP array pointer, FSHP count, and FSHP dictionary. Preserve FVTX and FMAT counts unless rebuilding/remapping those tables too.
BFRES Strings
BFRES strings are length-prefixed:
uint16_le byteLength
ascii/utf8 bytes
nul terminator
alignment padding as needed by containing data
Pointers in these files are absolute offsets to the two-byte length field, not to the first text character.
Renaming pattern learned from validated resaves:
Header full name: Custom_Model_Example.Custom_Model_Example
FMDL/model name: Custom_Model_Example
Model dictionary entry: Custom_Model_Example
Shape names may remain source-derived unless the task requires visible shape renames
MCPK / MeshCodec Compression
Validated MCPK details:
- Header magic:
MCPK.
- Bytes 4 and 5 are
0x01 0x01 in known-good files.
- Payload is a ZSTD frame with the standard ZSTD magic bytes stripped.
- Compression should disable content-size, checksum, and dictionary-ID metadata. ZSTD payloads in known-good files begin like
00 20..., 00 28..., or 00 30..., not Node's default 60... descriptor.
- Compression level 20 matched Switch-Toolbox-style output well enough for tools to load.
MCPK flags used in this project follow:
aligned = ((-decompressedLength % 0x1000) + 0x1000) % 0x1000
decompAligned = decompressedLength + aligned
flags = ((decompAligned >> 12) << 5) + 0x0c
Important distinction:
- A raw MCPK/ZSTD unwrap can produce a compact BFRES useful for inspection, but it is not an expanded edit source for vertex-buffer surgery.
- Geometry edits that read or rewrite vertex buffers require
FirstPlugin.MeshCodec.DecompressMeshCodec(Stream) or an equivalent expanded MeshCodec-decompressed .bfres.
- If an edit script reports out-of-range vertex data, tiny compact BFRES size, missing usable
FVTX buffers, or "expanded MeshCodec BFRES required", stop and fix decompression. Do not keep retrying the compact file or debug actor packs.
- Do not promote a new
.mc decompressor to proven unless final expanded outputs have non-collapsed vertex buffers, survive Toolbox normalization, compress back to MCPK, and match the existing validation checklist.
Switch Toolbox Normalization
Validated on Weapon_Sword_125: direct binary BFRES edits produced files that opened in Switch Toolbox but did not spawn in game. Opening and immediately saving in Toolbox fixed them. The automated equivalent is:
- Load the edited raw BFRES with Switch Toolbox assemblies.
- Preconfigure
Toolbox.Library.Runtime.ExecutableDir and Runtime.TotkGamePath before load, because headless use bypasses normal GUI config loading and can otherwise prompt for romfs.
- Load
Newtonsoft.Json.dll, Syroot.NintenTools.Bfres.dll, Syroot.NintenTools.NSW.Bfres.dll, Syroot.NintenTools.NSW.Bntx.dll, FirstPlugin.Plg.dll, and the matching System.Runtime.CompilerServices.Unsafe required by ZstdSharp.
- Create a dummy
Toolbox.Library.Forms.ObjectEditor and set Runtime.MainForm so BFRES.Load() can build its node tree.
- Save the loaded
FirstPlugin.BFRES back to raw .bfres.
- Compress that saved raw BFRES with
FirstPlugin.MeshCodec.CompressMeshCodec(Stream).
Geometry edits require one extra step between 5 and 6:
- Recenter serialized
FSHP.boundingBoxes and the related center/radius records to the edited shape vertices. Do this on the saved raw BFRES, not only on the in-memory Toolbox wrapper fields; wrapper edits can read as changed during the same process but fail to persist through BFRES.Save().
The headless loader must include format dependencies so Toolbox.Library.IO.STFileLoader.OpenFileFormat() can open .txtg textures. Without those dependencies, MeshCodec.PrepareTexToGo() may add null texture nodes and TexToGoFolder will throw ArgumentNullException: node.
Known dependency blocker:
- Some Switch Toolbox/ZstdSharp setups require
System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1 exactly. Unsafe assemblies with versions 5.0.0.0, 6.0.0.0, or 8.0.0.0 may not satisfy the binding.
- When that happens, ask for a working Toolbox setup, the exact compatible Unsafe DLL, or already expanded MeshCodec-decompressed
.bfres files. A task-local MCPK/ZSTD decompressor is not an acceptable substitute for vertex editing.
Expected normalized geometry-edit signs:
- Selected model has
FVTX count = 1 and FSHP count = 1.
_RLT is near the end of the BFRES, not left at the source external-buffer offset.
- Internal BFRES header and FMDL names match the output file.
- For every live edited
FSHP, serialized render boxes/spheres reopen centered on the actual vertex bounds, not on the source model's old geometry location.
- MCPK flags match the compact decompressed length.
- No stale physically present non-selected
FSHP record is needed after normalization.
Validation Checklist
After editing a model:
- Decompress
.bfres.mc and confirm MCPK magic and readable BFRES payload.
- For edited weapon models, confirm the final file is Toolbox-normalized/compact, not a one-frame compressed expanded BFRES.
- Confirm
FMDL name and BFRES header full name match the output filename.
- Confirm the selected
FSHP pointer and dictionary entry match the intended visible shape.
- Confirm
FSHP count is correct.
- For geometry edits, reopen the saved raw BFRES or MeshCodec-decompressed final
.bfres.mc and confirm FSHP render boxes/spheres are centered on the edited vertex bounds. Do not trust a same-process wrapper-field edit as proof.
- Confirm
FMAT count still includes every material index referenced by retained shapes.
- Confirm skeleton bind translations changed only when intended.
- For fuse-position or wrong-bone symptoms, confirm the final installed or merged
.bfres.mc round-trips to the expected BFRES state before blaming a proposed model fix. A source mod can be correct while the launched merged output is stale, and a BFRES-level discrepancy can be real without being the runtime lever.
- Open in Switch-Toolbox or another BFRES-aware viewer when available. If it crashes in material parsing, inspect material count/pointers first.
More Detail
Read references/bfres-field-notes.md when you need the project-specific discoveries, known failure modes, and examples from Weapon_Sword_106, 112, 113, and 124.
1---2name: totk-bfres-model3description: Work with Tears of the Kingdom BFRES model files and MeshCodec MCPK .bfres.mc containers: inspect sections, edit model/header/resource names, preserve BFRES table counts and pointers, adjust skeleton bones or vertex buffers, and recompress with game/tool-compatible MCPK. Use before or alongside higher-level TOTK model-editing skills. Cross-reference totk-texture-pipeline for BNTX/TXTG or DDS/ASTC texture work and totk-local-operator for compact local inspection/validation.4---56# TOTK BFRES Model Files78## Vanilla RomFS Rule910Use a vanilla TOTK `romfs` path already supplied in the current turn or registered project guidance. Otherwise ask the user for it before inspecting game files. Treat the path as read-only; do not search unrelated folders or guess a location.1112Use this skill for low-level Tears of the Kingdom model-file work: BFRES inspection, internal name edits, skeleton/vertex edits, and MCPK compression. It is a mechanics skill, not a task recipe.1314For texture container replacement, DDS/ASTC packaging, BNTX/TXTG validation, or non-model texture work, use `totk-texture-pipeline`. Use this BFRES skill only for model/material context, texture-name bindings, BFRES tables, skeletons, vertices, and resource identity.1516## Related TOTK Skills1718- Use `totk-toolkit` for shared reusable BFRES/MeshCodec and local inspection scripts before creating one-off tools.19- Use `totk-texture-pipeline` for BNTX/TXTG, DDS/ASTC, Switch Toolbox texture replacement, texture containers, and texture validation.20- Use `totk-local-operator` habits for compact inventories, offset summaries, hashes, and validation logs instead of loading binary dumps into context.2122## Core Rules2324- Treat source BFRES and vanilla/reference files as read-only. Write outputs to the requested working/output folder.25- When model outputs will be installed in a mod, ask whether the user wants additive unique paths, vanilla-path replacement, or to supersede another mod's path. Model path/name choices are often priority-sensitive and should follow the user's intent rather than defaulting to uniqueness.26- Prefer parsing the BFRES structure over hardcoded offsets. The weapon files studied here share offsets, but future files may not.27- Never patch counts casually. A Switch-Toolbox crash in `MaterialParserV10.LoadStrings` was caused by reducing `FMAT` count while a retained shape still referenced material index `1`.28- If output filename/model identity changes, update internal names too. At minimum repoint:29 - BFRES header full resource name pointer at `0x20`.30 - FMDL model name pointer at `FMDL + 0x08`.31 - FMDL model dictionary entry pointing to the same base name.32- If the new name is longer than the old string slot, append a new BFRES string and repoint references. Do not overwrite adjacent string table data.33- For final `.bfres.mc`, use real MCPK wrapping: `MCPK` header plus magicless ZSTD payload. Do not merely rename raw BFRES to `.bfres.mc`.34- For edited TOTK weapon models, do not trust a hand-patched expanded BFRES that merely opens in Switch Toolbox. The game may reject it. Normalize the edited raw BFRES through Switch Toolbox's BFRES save path, then MeshCodec-compress the saved compact BFRES to `.bfres.mc`.35- For any geometry-affecting edit, recenter serialized `FSHP` render bounds after the Toolbox save and before MeshCodec compression. Visible vertices and invisible render bounds are separate; stale boxes/spheres can make a correct mesh cull or disappear in camera/frustum tests. Use `totk-toolkit/scripts/toolbox_recenter_bfres_render_bounds.ps1` or a pipeline that calls it.36- If a model appears only after camera rotation or disappears at a sharp preview/frustum threshold, inspect the final BFRES render bounds before chasing actor AABB, physics, or material flags. Tested inventory-preview culling did not respond to ActorInfo-style AABB overlays, but was fixed by recentering serialized `FSHP` boxes/spheres on the edited geometry.37- For geometry edits, prefer Toolbox object-model edits over byte-stream surgery: load the expanded BFRES through `FirstPlugin.BFRES`, edit the selected `FSHP.vertices`, call `FSHP.SaveVertexBuffer()`, save through Toolbox, recenter serialized render bounds, then MeshCodec-compress.38- Binding-looking failures can come from model-layer state, not only actor `AttachmentParam` or RSDB bind-bone fields. If a fused model aligns on a bare hilt but misbinds on an intact weapon where `Bind_A`/`Bind_B`/`Bind_C`/`Bind_W` are separated from `Bind_Z`, inspect the final merged model BFRES before changing rotations. Compare against a working peer for `Shape.VertexSkinCount`, `Shape.SkinBoneIndices`, `VertexBuffer.VertexSkinCount`, `VertexBuffer.Attributes` such as `_i0`/`_w0`, `StrideArray`, `VertexBufferSizeArray`, and `Buffers`. Treat skinning or extra skin-index attributes as an investigation axis, not a proven standalone fix; one validated case showed that removing `_i0` and clearing skin counts changed the BFRES correctly but did not change the in-game bind behavior.39- Treat weapon visuals as state-specific evidence. Loose-world item view, inventory fused preview, wielded fused world view, and dropped/loose fused weapon view can each apply different render, hide-material, attachment, animation, or culling paths. A model that looks correct in one state does not prove the BFRES, bind policy, or rotation is correct in the others; record which state was tested before changing geometry or rotations.40- TKVSC-derived tooling does not replace this BFRES/MCPK workflow yet. Its public beta surface is useful for archive browsing and BNTX/TXTG texture preview, but `.bfres` and `.mc` support were still unfinished when reviewed. Use TKVSC-derived code only as read-only reconnaissance around models until it proves MeshCodec-equivalent expanded BFRES output.4142## Observed Switch BFRES FMDL Layout4344For the TOTK weapon BFRES files validated in this project:4546```text47FMDL + 0x20 = FVTX array pointer48FMDL + 0x28 = FSHP array pointer49FMDL + 0x30 = FSHP dictionary pointer50FMDL + 0x38 = FMAT array pointer51FMDL + 0x40 = FMAT dictionary pointer52FMDL + 0x68 = FVTX count53FMDL + 0x6a = FSHP count54FMDL + 0x6c = FMAT count55```5657When selecting one visible shape without rebuilding all tables, patch only the FSHP array pointer, FSHP count, and FSHP dictionary. Preserve `FVTX` and `FMAT` counts unless rebuilding/remapping those tables too.5859## BFRES Strings6061BFRES strings are length-prefixed:6263```text64uint16_le byteLength65ascii/utf8 bytes66nul terminator67alignment padding as needed by containing data68```6970Pointers in these files are absolute offsets to the two-byte length field, not to the first text character.7172Renaming pattern learned from validated resaves:7374```text75Header full name: Custom_Model_Example.Custom_Model_Example76FMDL/model name: Custom_Model_Example77Model dictionary entry: Custom_Model_Example78Shape names may remain source-derived unless the task requires visible shape renames79```8081## MCPK / MeshCodec Compression8283Validated MCPK details:8485- Header magic: `MCPK`.86- Bytes 4 and 5 are `0x01 0x01` in known-good files.87- Payload is a ZSTD frame with the standard ZSTD magic bytes stripped.88- Compression should disable content-size, checksum, and dictionary-ID metadata. ZSTD payloads in known-good files begin like `00 20...`, `00 28...`, or `00 30...`, not Node's default `60...` descriptor.89- Compression level 20 matched Switch-Toolbox-style output well enough for tools to load.9091MCPK flags used in this project follow:9293```text94aligned = ((-decompressedLength % 0x1000) + 0x1000) % 0x100095decompAligned = decompressedLength + aligned96flags = ((decompAligned >> 12) << 5) + 0x0c97```9899Important distinction:100101- A raw MCPK/ZSTD unwrap can produce a compact BFRES useful for inspection, but it is not an expanded edit source for vertex-buffer surgery.102- Geometry edits that read or rewrite vertex buffers require `FirstPlugin.MeshCodec.DecompressMeshCodec(Stream)` or an equivalent expanded MeshCodec-decompressed `.bfres`.103- If an edit script reports out-of-range vertex data, tiny compact BFRES size, missing usable `FVTX` buffers, or "expanded MeshCodec BFRES required", stop and fix decompression. Do not keep retrying the compact file or debug actor packs.104- Do not promote a new `.mc` decompressor to proven unless final expanded outputs have non-collapsed vertex buffers, survive Toolbox normalization, compress back to MCPK, and match the existing validation checklist.105106## Switch Toolbox Normalization107108Validated on `Weapon_Sword_125`: direct binary BFRES edits produced files that opened in Switch Toolbox but did not spawn in game. Opening and immediately saving in Toolbox fixed them. The automated equivalent is:1091101. Load the edited raw BFRES with Switch Toolbox assemblies.1112. Preconfigure `Toolbox.Library.Runtime.ExecutableDir` and `Runtime.TotkGamePath` before load, because headless use bypasses normal GUI config loading and can otherwise prompt for romfs.1123. Load `Newtonsoft.Json.dll`, `Syroot.NintenTools.Bfres.dll`, `Syroot.NintenTools.NSW.Bfres.dll`, `Syroot.NintenTools.NSW.Bntx.dll`, `FirstPlugin.Plg.dll`, and the matching `System.Runtime.CompilerServices.Unsafe` required by `ZstdSharp`.1134. Create a dummy `Toolbox.Library.Forms.ObjectEditor` and set `Runtime.MainForm` so `BFRES.Load()` can build its node tree.1145. Save the loaded `FirstPlugin.BFRES` back to raw `.bfres`.1156. Compress that saved raw BFRES with `FirstPlugin.MeshCodec.CompressMeshCodec(Stream)`.116117Geometry edits require one extra step between 5 and 6:118119- Recenter serialized `FSHP.boundingBoxes` and the related center/radius records to the edited shape vertices. Do this on the saved raw BFRES, not only on the in-memory Toolbox wrapper fields; wrapper edits can read as changed during the same process but fail to persist through `BFRES.Save()`.120121The headless loader must include format dependencies so `Toolbox.Library.IO.STFileLoader.OpenFileFormat()` can open `.txtg` textures. Without those dependencies, `MeshCodec.PrepareTexToGo()` may add null texture nodes and `TexToGoFolder` will throw `ArgumentNullException: node`.122123Known dependency blocker:124125- Some Switch Toolbox/ZstdSharp setups require `System.Runtime.CompilerServices.Unsafe, Version=4.0.4.1` exactly. Unsafe assemblies with versions `5.0.0.0`, `6.0.0.0`, or `8.0.0.0` may not satisfy the binding.126- When that happens, ask for a working Toolbox setup, the exact compatible Unsafe DLL, or already expanded MeshCodec-decompressed `.bfres` files. A task-local MCPK/ZSTD decompressor is not an acceptable substitute for vertex editing.127128Expected normalized geometry-edit signs:129130- Selected model has `FVTX count = 1` and `FSHP count = 1`.131- `_RLT` is near the end of the BFRES, not left at the source external-buffer offset.132- Internal BFRES header and FMDL names match the output file.133- For every live edited `FSHP`, serialized render boxes/spheres reopen centered on the actual vertex bounds, not on the source model's old geometry location.134- MCPK flags match the compact decompressed length.135- No stale physically present non-selected `FSHP` record is needed after normalization.136137## Validation Checklist138139After editing a model:140141- Decompress `.bfres.mc` and confirm `MCPK` magic and readable BFRES payload.142- For edited weapon models, confirm the final file is Toolbox-normalized/compact, not a one-frame compressed expanded BFRES.143- Confirm `FMDL` name and BFRES header full name match the output filename.144- Confirm the selected `FSHP` pointer and dictionary entry match the intended visible shape.145- Confirm `FSHP` count is correct.146- For geometry edits, reopen the saved raw BFRES or MeshCodec-decompressed final `.bfres.mc` and confirm `FSHP` render boxes/spheres are centered on the edited vertex bounds. Do not trust a same-process wrapper-field edit as proof.147- Confirm `FMAT` count still includes every material index referenced by retained shapes.148- Confirm skeleton bind translations changed only when intended.149- For fuse-position or wrong-bone symptoms, confirm the final installed or merged `.bfres.mc` round-trips to the expected BFRES state before blaming a proposed model fix. A source mod can be correct while the launched merged output is stale, and a BFRES-level discrepancy can be real without being the runtime lever.150- Open in Switch-Toolbox or another BFRES-aware viewer when available. If it crashes in material parsing, inspect material count/pointers first.151152## More Detail153154Read [references/bfres-field-notes.md](references/bfres-field-notes.md) when you need the project-specific discoveries, known failure modes, and examples from `Weapon_Sword_106`, `112`, `113`, and `124`.