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>
217 lines
5.9 KiB
Plaintext
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");
|
|
}
|
|
}
|