# Totk Bfres Model

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

- Skill: `cargocult-mods/totk-bfres-model` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add cargocult-mods/totk-bfres-model`
- Raw SKILL.md: https://api.skillmd.com/api/skills/cargocult-mods/totk-bfres-model/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: cargocult-mods (https://skillmd.com/u/cargocult-mods)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/cargocult-mods/totk-bfres-model

---


# 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:

```text
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:

```text
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:

```text
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:

```text
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:

1. Load the edited raw BFRES with Switch Toolbox assemblies.
2. Preconfigure `Toolbox.Library.Runtime.ExecutableDir` and `Runtime.TotkGamePath` before load, because headless use bypasses normal GUI config loading and can otherwise prompt for romfs.
3. 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`.
4. Create a dummy `Toolbox.Library.Forms.ObjectEditor` and set `Runtime.MainForm` so `BFRES.Load()` can build its node tree.
5. Save the loaded `FirstPlugin.BFRES` back to raw `.bfres`.
6. 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](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`.

