XPlor/definitions/volition/mesh.xscript
njohnson 908100f487 Enhance Volition format definitions for Saints Row series
Major improvements to VPP, PEG, and ASM parsers:

vpp.xscript:
- Support all VPP versions (4-10) for Saints Row 1-4 and Red Faction
- Add container type detection (str2, packfile, etc.)
- Improved compression handling (zlib, LZO, auto-detection)
- Better recursive parsing of nested archives

peg.xscript:
- Full PEG texture container parsing
- Support for multiple texture formats (DXT1, DXT3, DXT5, etc.)
- Xbox 360 and PC format variants

asm.xscript:
- Animation state machine container parsing

New format definitions:
- anim.xscript: Animation data
- audio.xscript: Audio containers (bank files)
- mesh.xscript: 3D mesh geometry
- morph.xscript: Morph targets
- rig.xscript: Skeletal rigs
- sim.xscript: Simulation data
- zone.xscript: Level zone data

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-12 20:53:59 -05:00

126 lines
3.5 KiB
Plaintext

// Volition Mesh Format (csmesh/ccmesh/clmesh)
// Used by: Saints Row 3/4, Red Faction series
// Magic: 0x00043854 (v_file_header signature)
// c* = CPU header data, g* = GPU geometry data
// ccmesh = character (rigged), csmesh = static, clmesh = level
type volition_mesh ui("Volition Mesh", root) byteorder LE
{
criteria {
require _ext == "csmesh_pc" || _ext == "csmesh_xbox2" || _ext == "csmesh_ps3" ||
_ext == "ccmesh_pc" || _ext == "ccmesh_xbox2" || _ext == "ccmesh_ps3" ||
_ext == "clmesh_pc" || _ext == "clmesh_xbox2" || _ext == "clmesh_ps3";
require u32at(0) == 0x00043854;
}
// Detect platform
is_xbox = ends_with(_ext, "xbox2");
is_ps3 = ends_with(_ext, "ps3");
if (is_xbox || is_ps3) {
parse_here("volition_mesh_be");
} else {
parse_here("volition_mesh_impl");
}
}
// Big-endian wrapper for Xbox 360/PS3
type volition_mesh_be ui("Mesh") byteorder BE
{
parse_here("volition_mesh_impl");
}
// Shared implementation
type volition_mesh_impl ui("Mesh")
{
// Detect mesh type from extension
is_character = contains(_ext, "ccmesh");
is_static = contains(_ext, "csmesh");
is_level = contains(_ext, "clmesh");
mesh_type = "Unknown Mesh";
if (is_character) { mesh_type = "Character Mesh (Rigged)"; }
if (is_static) { mesh_type = "Static Mesh"; }
if (is_level) { mesh_type = "Level Mesh"; }
set_name(mesh_type);
// v_file_header (streaming reference header)
u32 signature ui("Signature"); // 0x00043854
u32 data_size ui("Data Size");
u32 unknown_08 ui("Unknown 0x08");
u32 file_count ui("File Count");
// Mesh info header
u32 mesh_magic ui("Mesh Magic"); // 0x424bdood
u32 mesh_version ui("Version"); // Usually 0x2a (42)
// Model data block
u32 nine ui("Nine"); // Always 9
u32 crc ui("CRC");
u32 model_data_size ui("Model Data Size");
u32 gmesh_size ui("GPU Mesh Size");
u32 flags ui("Flags");
u32 indices_count ui("Index Count");
u32 index_stride ui("Index Stride");
u32 bones_count ui("Bone Count");
f32 scale_x ui("Scale X");
f32 scale_y ui("Scale Y");
f32 scale_z ui("Scale Z");
u32 vertex_count ui("Vertex Count");
u32 vertex_stride ui("Vertex Stride");
// Bounding sphere
f32 bound_x ui("Bound Center X");
f32 bound_y ui("Bound Center Y");
f32 bound_z ui("Bound Center Z");
f32 bound_radius ui("Bound Radius");
// Texture name count
u32 texture_count ui("Texture Count");
// Parse texture names
if (texture_count > 0 && texture_count < 100) {
tex_idx = 0;
repeat(texture_count) {
tex_name = cstring();
ctx_set("_mesh_tex_name", tex_name);
ctx_set("_mesh_tex_idx", tex_idx);
tex_entry = bytesat(0, 1) |> parse volition_mesh_texture_ref;
push("textures", tex_entry);
tex_entry = 0;
tex_idx = tex_idx + 1;
}
}
// Summary info
type_display = mesh_type ui("Mesh Type");
total_file_size = size() ui("File Size");
}
// Texture reference entry
type volition_mesh_texture_ref ui("Texture Reference")
{
tex_name = ctx_get("_mesh_tex_name");
set_name(tex_name);
index = ctx_get("_mesh_tex_idx") ui("Index");
name = tex_name ui("Texture Name");
}
// Nested mesh type for VPP parsing
type volition_mesh_nested ui("Mesh") byteorder LE
{
file_name = ctx_get("_vpp_file_name");
set_name(file_name);
// Detect platform from context
is_xbox = ctx_get("_vpp_is_xbox");
is_ps3 = ctx_get("_vpp_is_ps3");
// Use truthiness check for boolean context values
if (is_xbox || is_ps3) {
parse_here("volition_mesh_be");
} else {
parse_here("volition_mesh_impl");
}
}