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

149 lines
4.7 KiB
Plaintext

// Volition Zone Format (czh/czn/gzn)
// Used by: Saints Row 3/4
// CZH = Zone Header, CZN = Zone CPU Data, GZN = Zone GPU Data
// v_file_header signature: 0x3854
// world_zone_header signature: 'Z3RS' (0x5A335253)
type volition_zone ui("Volition Zone", root) byteorder LE
{
criteria {
require _ext == "czh_pc" || _ext == "czh_xbox2" || _ext == "czh_ps3" ||
_ext == "czn_pc" || _ext == "czn_xbox2" || _ext == "czn_ps3" ||
_ext == "gzn_pc" || _ext == "gzn_xbox2" || _ext == "gzn_ps3";
// Check for v_file_header signature
require u16at(0) == 0x3854;
}
is_header = contains(_ext, "czh");
is_cpu_data = contains(_ext, "czn");
is_gpu_data = contains(_ext, "gzn");
is_xbox = ends_with(_ext, "xbox2");
is_ps3 = ends_with(_ext, "ps3");
zone_type = "Zone";
if (is_header) { zone_type = "Zone Header"; }
if (is_cpu_data) { zone_type = "Zone Data (CPU)"; }
if (is_gpu_data) { zone_type = "Zone Data (GPU)"; }
if (is_xbox) {
set_name(zone_type + " (Xbox 360)");
} else if (is_ps3) {
set_name(zone_type + " (PS3)");
} else {
set_name(zone_type + " (PC)");
}
ctx_set("_zone_is_header", is_header);
ctx_set("_zone_is_cpu", is_cpu_data);
ctx_set("_zone_is_gpu", is_gpu_data);
parse_here("volition_zone_impl");
}
type volition_zone_impl ui("Zone")
{
// v_file_header
u16 vfh_signature ui("VFH Signature"); // 0x3854
u16 vfh_version ui("VFH Version");
u32 reference_data_size ui("Reference Data Size");
u32 reference_data_start ui("Reference Data Start");
u32 reference_count ui("Reference Count");
u8 initialized ui("Initialized");
skip(15); // padding
is_header = ctx_get("_zone_is_header");
is_gpu = ctx_get("_zone_is_gpu");
// Check for world_zone_header signature
if (pos() < size() - 8) {
wzh_sig_check = u32at(pos());
// 'Z3RS' = 0x5A335253 (LE) or 'SR3Z' = 0x53523358
if (wzh_sig_check == 0x5A335253 || wzh_sig_check == 0x53523358) {
u32 wzh_signature ui("Zone Signature"); // Z3RS
u32 wzh_version ui("Zone Version");
// Parse sections if this is zone data (not just header)
if (is_header != 1 && pos() < size() - 8) {
section_count = 0;
while (pos() < size() - 8 && section_count < 100) {
section_id_raw = u32at(pos());
// Valid section IDs are in 0x2233+ range
if (section_id_raw >= 0x2233 && section_id_raw < 0x3000) {
u32 section_id;
u32 cpu_size;
has_gpu = (section_id_raw & 0x80000000) != 0;
actual_id = section_id_raw & 0x7FFFFFFF;
gpu_size_val = 0;
if (has_gpu) {
u32 gpu_size;
gpu_size_val = gpu_size;
}
ctx_set("_zone_section_id", actual_id);
ctx_set("_zone_section_cpu", cpu_size);
ctx_set("_zone_section_gpu", gpu_size_val);
section_entry = bytesat(0, 1) |> parse volition_zone_section;
push("sections", section_entry);
section_entry = 0;
// Skip section data
skip(cpu_size);
section_count = section_count + 1;
} else {
break;
}
}
total_sections = section_count ui("Section Count");
}
}
}
zone_type_display = "Unknown";
if (is_header == 1) { zone_type_display = "Zone Header (CZH)"; }
if (ctx_get("_zone_is_cpu") == 1) { zone_type_display = "Zone CPU Data (CZN)"; }
if (is_gpu == 1) { zone_type_display = "Zone GPU Data (GZN)"; }
type_display = zone_type_display ui("Zone Type");
ref_count_display = reference_count ui("References");
file_size = size() ui("File Size");
}
type volition_zone_section ui("Section")
{
section_id = ctx_get("_zone_section_id");
cpu_size = ctx_get("_zone_section_cpu");
gpu_size = ctx_get("_zone_section_gpu");
// Map section ID to name
section_name = "Unknown Section";
if (section_id == 0x2233) { section_name = "Reference Geometry"; }
if (section_id == 0x2234) { section_name = "Objects"; }
if (section_id == 0x2235) { section_name = "Navigation Mesh"; }
if (section_id == 0x2236) { section_name = "Traffic Data"; }
if (section_id == 0x2244) { section_name = "Heightmap"; }
if (section_id == 0x2247) { section_name = "Water Volumes"; }
set_name(section_name);
id = section_id ui("Section ID");
cpu_data_size = cpu_size ui("CPU Size");
gpu_data_size = gpu_size ui("GPU Size");
}
// Nested type for VPP parsing
type volition_zone_nested ui("Zone") byteorder LE
{
file_name = ctx_get("_vpp_file_name");
set_name(file_name);
ctx_set("_zone_is_header", contains(file_name, ".czh"));
ctx_set("_zone_is_cpu", contains(file_name, ".czn"));
ctx_set("_zone_is_gpu", contains(file_name, ".gzn"));
parse_here("volition_zone_impl");
}