Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

ffxii-tza-tools

File format specifications and standalone Python tools for modding FINAL FANTASY XII The Zodiac Age (PC). Everything was reverse-engineered from the game binary and checked offline against every matching file in the game archive. Covered today: character animations (.ca, 3164 files), character models (.cm, 1066 files) and the HD assets they depend on (.phyre models, textures and shader effects).

From the game archive to Blender and back

Start here

Two file types, one box format. Every character is a .cm file (the model: skeleton, mesh, textures, parameters) plus several .ca files (the animations). Both are @FRP containers: a header, a list of offsets, then chunks. Only the chunk set differs.

The chunks of a .cm and of a .ca

Memory aid: chunks whose name starts with @FM (M for Motion) are animation, so they live in .ca. Everything else is the model, so .cm.

Three ways in, depending on what you want:

I want to… Read Run
understand how FFXII animations and models work, from zero docs/character-animations.md python tools/decode_ca.py c1004_00.ca --summary
view a character in Blender that guide, §5 python tools/export_ca_gltf.py c1004_00.ca -o vaan.glb
write my own tool or check a byte docs/formats/ (one sheet per chunk, with a glossary) python tools/frp_tool.py list c1004.cm

Every tool prints its usage with --help.

Requirements

  • Python 3.9+, standard library only (pip install Pillow for PNG output).
  • Game files extracted from FFXII_TZA.vbf with any community VBF extractor. This repo contains no game assets — bring your own legally-owned copy.

Quickstart (3 commands)

python tools/decode_ca.py      c1004_00.ca --summary        # what's inside an animation file?
python tools/export_ca_gltf.py c1004_00.ca -o vaan_00.glb   # play it in Blender
python tools/dump_cm_mesh.py   c1004.cm --glb vaan_ps2.glb  # the PS2 model, skinned, textured, animated

The tools

Look inside a file

decode_ca.py: what is inside an animation file

Tool Usage What it does
decode_ca.py <file.ca | dir> [--summary] [--curves [--motion NAME]] [--commands [--motion NAME]] [--validate-curves] [--validate-commands] [--json] Full .ca decoder: container, chunk list, motion names, every keyframe curve (with the constant values of the unanimated channels), and the command scripts with every opcode named. Also lists the chunks of any .cm. Validated on all 3164 game files.
frp_tool.py list <file.ca | file.cm> Quick chunk table of any @FRP container.
dump_cm_skeleton.py <file.cm> [--flat] Bone hierarchy (ids + parent tree).
dump_cm_bindpose.py <file.cm> [--json] Rest-pose translation / rotation / scale of every bone.
dump_cm_for_table.py <file.cm> The outfit table: actions, colour variants, which submesh is drawn with which texture.
dump_cm_texanim.py <file.cm | dir> [--json] [--summary] The eye-blink / facial-patch table (strip, target, rectangle, cells, sequences).
dump_cm_textures.py <file.cm> --list | --segmap [--action A] [--variant V] Texture descriptors and palettes, or the per-segment (texture, palette) map of an outfit.
dump_cm_mesh.py <file.cm> --stats | --check | --sweep Decodes and validates the PS2 mesh (1045/1045 files, 0 errors).
decode_phyre_mesh.py <model.dae.phyre> --info Header and segment dump of an HD mesh.
decode_fx_phyre.py list <effect.fx.phyre> / sweep [shader_dir] Render-pass names and their D3D11 blend / depth / rasterizer states (what decides double-sided materials).

Export to Blender / three.js (glTF .glb)

export_ca_gltf.py: animation + model to one .glb

Tool Usage What it does
export_ca_gltf.py <file.ca> [--cm <file.cm>] [-o out.glb] [--motion NAME] [--no-merge] [--fps N] Animations: skeleton from the sibling .cm (auto-detected) + one glTF animation per motion. Left/right locomotion halves are merged into full cycles with the game's crossfade; the secondary bones the game simulates itself are held at rest, like in game.
dump_cm_mesh.py <file.cm> --glb out.glb [--lod N] [--action N] [--pc-ref <hd.glb>] [--no-mirror] [--no-skin] [--no-tex] [--no-anims] [--obj out.obj] The PS2 model from the .cm alone: mesh per LOD with the mirrored half rebuilt, skeleton and skinning, vertex colours, the PS2 textures of the chosen outfit embedded as PNG, animations from the sibling .ca files. --pc-ref with the model's export_glb.py output gives the exact mirror skinning and the right texture orientation on the few models TZA rewrote.
dump_cm_textures.py <file.cm> --png <outdir> [--row N] The PS2 textures as PNG, one per (texture, palette) the model draws; --row picks a battle-status palette row.
export_glb.py <model.dae.phyre> [-o out.glb] [--cm <file.cm>] [--ca <file.ca>] [--action N] [--variant N] [--weapon <w.dae.phyre>] [--shield <s.dae.phyre>] [--no-skin] [--no-anims] The HD model: mesh, skeleton, textures, tangents and vertex colours from the DX11 .dae.phyre, textured through the .cm outfit table, materials single- or double-sided per segment like the game. HD close-up parts supported. Validated on the whole model library.
decode_phyre_mesh.py <model.dae.phyre> [-o out.obj] Static HD mesh to OBJ (no skinning).
decode_dds_phyre.py <tex.dds.phyre> [-o out.png] [--info] HD texture to PNG; cube maps to DDS.

Blender note: materials the game blends (hair, cloth with authored partial transparency) are exported as glTF BLEND. Blender renders BLEND without the depth pre-pass the engine uses, so overlapping geometry can look see-through from some angles. Set those materials' Render Method to Dithered (Material Properties > Settings) and they display like in game.

Edit and rebuild

frp_tool.py: split, change one chunk, pack

Tool Usage What it does
frp_tool.py split / pack split <file> -o <dir> then pack <dir> -o <file> [--verify <original>] Explodes a .ca / .cm into one file per chunk (+ manifest.json) and rebuilds it. Unmodified rebuilds are byte-identical (proven on all 3164 .ca + 1066 .cm). Warns about chunk combinations that crash the game.
patch_ca_curve.py <file.ca> --motion NAME --bone N --channel N (--add X | --mul X) [-o out.ca] Shifts or amplifies one animation curve in place, without re-encoding anything.
encode_ca.py roundtrip <file.ca> / rebuild <file.ca> -o out.ca Full .ca re-encoder (custom keyframes possible from Python). Round-trip is bit-exact on every file in the game.

Helper modules, not meant to be run: phyre_links.py, phyre_materials.py, phyre_reflection.py (link tables, per-segment materials and type ids of the .phyre container, used by export_glb.py). tools/model_names.json maps model ids to display names (c1004 → Vaan).

Loading a modified file into the game

Use the FF12 External File Loader (the standard mod loader). Put your rebuilt .ca or .cm at its archive path inside your mod's mods/deploy/ff12data/ folder, e.g. mods/deploy/ff12data/ps2data/obj_finish/in/chara/c10/c1004/c1004_00.ca. The game never checksums these files.

Status

Every chunk of .ca and .cm is documented with its validation scope in docs/formats/. Reading and exporting is solved and verified on every file in the game; editing existing animations works; creating brand-new animations from scratch is possible from Python (keyframe overrides in encode_ca.py) but the "edit in Blender, press a button" workflow is not there yet. The embedded .efx effect images of gadget models are out of scope here.

Versioning follows SemVer — see CHANGELOG.md. Bugs and spec errors: CONTRIBUTING.md.

License

MIT. FINAL FANTASY XII is a trademark of Square Enix — this project is unaffiliated fan research and redistributes no game content.

About

File format specs and Python tools for modding FINAL FANTASY XII The Zodiac Age (PC): animations, models, textures, shaders. Decode, export to glTF, edit, split/pack.

Topics

Resources

Contributing

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages