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>
149 lines
4.7 KiB
Plaintext
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");
|
|
}
|