XPlor/definitions/volition/asm.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

217 lines
5.9 KiB
Plaintext

// Volition ASM Assembly/Manifest Format
// Used by: Saints Row 3/4, Red Faction series
// Magic: 0xBEEFFEED (LE) or 0xEDFEEFBE (BE)
// Contains asset container and primitive definitions
// Root dispatcher - detects endianness and delegates
type volition_asm ui("Volition ASM File", root) byteorder LE
{
criteria {
require _ext == "asm_pc" || _ext == "asm" || _ext == "asm_xbox2" || _ext == "asm_ps3";
le_magic = u32at(0);
require le_magic == 0xBEEFFEED || le_magic == 0xEDFEEFBE;
}
// Detect platform
is_xbox = ends_with(_ext, "xbox2");
is_ps3 = ends_with(_ext, "ps3");
// Detect endianness from magic
magic_check = u32at(0);
if (magic_check == 0xEDFEEFBE) {
if (is_ps3) {
set_name("ASM File (PS3)");
} else {
set_name("ASM File (Xbox 360)");
}
parse_here("volition_asm_be");
} else {
set_name("ASM File (PC)");
parse_here("volition_asm_impl");
}
}
// BE wrapper - explicit byteorder needed to override inherited LE
type volition_asm_be ui("ASM BE") byteorder BE
{
parse_here("volition_asm_impl");
}
// Shared implementation - inherits byteorder from caller
type volition_asm_impl ui("ASM")
{
u32 magic ui("Magic");
u16 version ui("Version");
u16 container_count ui("Container Count");
// Version 8+ has type registries
if (version >= 8) {
u32 allocator_type_count ui("Allocator Type Count");
alloc_idx = 0;
repeat(allocator_type_count) {
u16 at_name_len;
at_name = ascii(read(at_name_len));
u8 at_id;
ctx_set("_asm_type_name", at_name);
ctx_set("_asm_type_id", at_id);
at_entry = bytesat(0, 1) |> parse volition_asm_type_entry;
push("allocator_types", at_entry);
at_entry = 0;
alloc_idx = alloc_idx + 1;
}
u32 primitive_type_count ui("Primitive Type Count");
prim_idx = 0;
repeat(primitive_type_count) {
u16 pt_name_len;
pt_name = ascii(read(pt_name_len));
u8 pt_id;
ctx_set("_asm_type_name", pt_name);
ctx_set("_asm_type_id", pt_id);
pt_entry = bytesat(0, 1) |> parse volition_asm_type_entry;
push("primitive_types", pt_entry);
pt_entry = 0;
prim_idx = prim_idx + 1;
}
u32 container_type_count ui("Container Type Count");
cont_idx = 0;
repeat(container_type_count) {
u16 ct_name_len;
ct_name = ascii(read(ct_name_len));
u8 ct_id;
ctx_set("_asm_type_name", ct_name);
ctx_set("_asm_type_id", ct_id);
ct_entry = bytesat(0, 1) |> parse volition_asm_type_entry;
push("container_types", ct_entry);
ct_entry = 0;
cont_idx = cont_idx + 1;
}
}
// Parse container entries
c_idx = 0;
repeat(container_count) {
u16 c_name_len;
c_name = ascii(read(c_name_len));
u8 c_type;
u16 c_flags;
u16 c_primitive_count;
u32 c_data_offset;
if (version >= 10) {
u16 c_parent_len;
c_parent = ascii(read(c_parent_len));
}
u32 c_extra_len;
if (c_extra_len > 0) {
c_extra = read(c_extra_len);
}
if (version < 9) {
i32 c_old_size;
}
u32 c_compressed_size;
has_sizes = ((c_flags & 0x80) != 0) || (version >= 11);
if (has_sizes) {
sizes_idx = 0;
repeat(c_primitive_count) {
if (version >= 9) {
i32 ps_cpu_size;
}
i32 ps_gpu_size;
sizes_idx = sizes_idx + 1;
}
}
// Parse primitives and collect into array
primitives_list = make_list();
prim_idx = 0;
repeat(c_primitive_count) {
u16 p_name_len;
p_name = ascii(read(p_name_len));
u8 p_type;
u8 p_allocator;
u8 p_flags;
u8 p_split_index;
i32 p_cpu_size;
i32 p_gpu_size;
u8 p_unknown7;
// Create primitive entry
ctx_set("_prim_name", p_name);
ctx_set("_prim_type", p_type);
ctx_set("_prim_cpu_size", p_cpu_size);
ctx_set("_prim_gpu_size", p_gpu_size);
prim_entry = bytesat(0, 1) |> parse volition_asm_primitive;
push(primitives_list, prim_entry);
prim_entry = 0;
prim_idx = prim_idx + 1;
}
// Create container child with primitives
ctx_set("_asm_container_name", c_name);
ctx_set("_asm_container_prim_count", c_primitive_count);
ctx_set("_asm_container_primitives", primitives_list);
dummy = bytesat(0, 1);
container_child = dummy |> parse volition_asm_container;
push("containers", container_child);
container_child = 0; // Clear to avoid duplicate tree node
c_idx = c_idx + 1;
}
total_containers = container_count ui("Total Containers");
file_size = size() ui("File Size");
}
// Container child - inherits byteorder from parent
type volition_asm_container ui("Container")
{
container_name = ctx_get("_asm_container_name");
set_name(container_name);
prim_count = ctx_get("_asm_container_prim_count");
primitive_count = prim_count ui("Primitive Count");
primitives = ctx_get("_asm_container_primitives");
}
// Primitive entry - inherits byteorder from parent
type volition_asm_primitive ui("Primitive")
{
prim_name = ctx_get("_prim_name");
set_name(prim_name);
type_id = ctx_get("_prim_type") ui("Type");
cpu_size = ctx_get("_prim_cpu_size") ui("CPU Size");
gpu_size = ctx_get("_prim_gpu_size") ui("GPU Size");
}
// Type registry entry (allocator, primitive, or container type)
type volition_asm_type_entry ui("Type Entry")
{
type_name = ctx_get("_asm_type_name");
set_name(type_name);
name = type_name ui("Name");
id = ctx_get("_asm_type_id") ui("ID");
}
// Nested ASM type - for parsing ASM files within VPP archives
// No criteria block needed since called directly via parse()
type volition_asm_nested ui("ASM Manifest") byteorder LE
{
file_name = ctx_get("_vpp_file_name");
// Detect endianness from magic
magic_check = u32at(0);
if (magic_check == 0xEDFEEFBE) {
set_name(file_name + " (BE)");
parse_here("volition_asm_be");
} else {
set_name(file_name);
parse_here("volition_asm_impl");
}
}