- Volition VPP: Unified BE/LE types using inheritance pattern - THQA PAK: Child types now inherit byte order from parent - Various XScript definition updates and fixes Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
208 lines
6.3 KiB
Plaintext
208 lines
6.3 KiB
Plaintext
// Volition PEG Texture Format
|
|
// Used by: Saints Row 2, Red Faction: Guerrilla, Red Faction: Armageddon
|
|
// Magic: 0x564B4547 ("GEKV" / "VKEG")
|
|
// Versions: 10, 13
|
|
// Frame size: 48 bytes (0x30)
|
|
|
|
type volition_peg ui("Volition PEG Texture", root) byteorder LE
|
|
{
|
|
criteria {
|
|
require _ext == "peg_pc" || _ext == "g_peg_pc" || _ext == "cvbm_pc" || _ext == "peg";
|
|
// Check magic in both endianness
|
|
le_magic = u32at(0);
|
|
require le_magic == 0x564B4547 || le_magic == 0x47454B56;
|
|
}
|
|
|
|
// Detect endianness from magic
|
|
magic_check = u32at(0);
|
|
if (magic_check == 0x47454B56) {
|
|
_is_big_endian = 1;
|
|
set_display("PEG Texture (Big Endian)");
|
|
} else {
|
|
_is_big_endian = 0;
|
|
}
|
|
|
|
magic = hex(read(4)) ui("Magic");
|
|
u16 version ui("Version");
|
|
u16 platform ui("Platform");
|
|
|
|
if (version == 10) {
|
|
parse_here("volition_peg_v10");
|
|
} else if (version == 13) {
|
|
parse_here("volition_peg_v13");
|
|
} else {
|
|
unknown_version = version ui("Unsupported Version");
|
|
set_preview("peg_data.bin", read(size() - pos()));
|
|
set_viewer("hex");
|
|
}
|
|
}
|
|
|
|
// PEG Version 10 - Saints Row 2
|
|
type volition_peg_v10 ui("PEG V10") byteorder LE
|
|
{
|
|
set_name("PEG Texture (V10)");
|
|
|
|
// Header continues after magic/version/platform (already read 8 bytes)
|
|
u32 header_size ui("Header Size");
|
|
u32 data_size ui("Data Size");
|
|
u16 texture_count ui("Texture Count");
|
|
u16 unknown0E ui("Unknown 0x0E");
|
|
u16 frame_count ui("Frame Count");
|
|
u8 unknown12 ui("Unknown 0x12");
|
|
u8 unknown13 ui("Unknown 0x13");
|
|
|
|
// Parse frames (48 bytes each)
|
|
skip_tree("frames");
|
|
frame_idx = 0;
|
|
repeat(frame_count) {
|
|
u32 f_data_offset;
|
|
u16 f_width;
|
|
u16 f_height;
|
|
u32 f_format;
|
|
u32 f_unknown0C;
|
|
u16 f_frame_count;
|
|
u16 f_flags;
|
|
u32 f_name_offset;
|
|
u32 f_unknown18;
|
|
u32 f_size;
|
|
u32 f_unknown20;
|
|
u32 f_unknown24;
|
|
u32 f_unknown28;
|
|
u32 f_unknown2C;
|
|
|
|
ctx_set("_peg_frame_idx", frame_idx);
|
|
ctx_set("_peg_frame_offset", f_data_offset);
|
|
ctx_set("_peg_frame_width", f_width);
|
|
ctx_set("_peg_frame_height", f_height);
|
|
ctx_set("_peg_frame_format", f_format);
|
|
ctx_set("_peg_frame_size", f_size);
|
|
ctx_set("_peg_frame_flags", f_flags);
|
|
|
|
// Determine format name
|
|
if (f_format == 400) { format_name = "DXT1"; }
|
|
else if (f_format == 401) { format_name = "DXT3"; }
|
|
else if (f_format == 402) { format_name = "DXT5"; }
|
|
else if (f_format == 403) { format_name = "R5G6B5"; }
|
|
else if (f_format == 404) { format_name = "A1R5G5B5"; }
|
|
else if (f_format == 405) { format_name = "A4R4G4B4"; }
|
|
else if (f_format == 406) { format_name = "R8G8B8"; }
|
|
else if (f_format == 407) { format_name = "A8R8G8B8"; }
|
|
else if (f_format == 408) { format_name = "V8U8"; }
|
|
else if (f_format == 409) { format_name = "CxV8U8"; }
|
|
else if (f_format == 410) { format_name = "A8"; }
|
|
else if (f_format == 603) { format_name = "Unknown_603"; }
|
|
else { format_name = "Unknown"; }
|
|
ctx_set("_peg_frame_format_name", format_name);
|
|
|
|
dummy = bytesat(0, 1);
|
|
frame_child = dummy |> parse volition_peg_frame_row;
|
|
push("frames", frame_child);
|
|
|
|
frame_idx = frame_idx + 1;
|
|
}
|
|
|
|
// Parse texture names (null-terminated strings)
|
|
name_idx = 0;
|
|
repeat(texture_count) {
|
|
tex_name = cstring();
|
|
ctx_set("_peg_tex_name_" + name_idx, tex_name);
|
|
name_idx = name_idx + 1;
|
|
}
|
|
|
|
ui_table("frames", "frames", "Index,Width,Height,Format,Size,Flags");
|
|
|
|
total_frames = frame_count ui("Total Frames");
|
|
total_textures = texture_count ui("Total Textures");
|
|
file_size = size() ui("File Size");
|
|
}
|
|
|
|
// PEG Version 13 - Red Faction: Armageddon
|
|
type volition_peg_v13 ui("PEG V13") byteorder LE
|
|
{
|
|
set_name("PEG Texture (V13)");
|
|
|
|
// Header continues after magic/version/platform (already read 8 bytes)
|
|
u32 header_size ui("Header Size");
|
|
u32 data_size ui("Data Size");
|
|
u16 texture_count ui("Texture Count");
|
|
u16 unknown0E ui("Unknown 0x0E");
|
|
u16 frame_count ui("Frame Count");
|
|
u16 unknown12 ui("Unknown 0x12");
|
|
|
|
// Parse frames (48 bytes each - same as V10)
|
|
skip_tree("frames");
|
|
frame_idx = 0;
|
|
repeat(frame_count) {
|
|
u32 f_data_offset;
|
|
u16 f_width;
|
|
u16 f_height;
|
|
u32 f_format;
|
|
u32 f_unknown0C;
|
|
u16 f_frame_count;
|
|
u16 f_flags;
|
|
u32 f_name_offset;
|
|
u32 f_unknown18;
|
|
u32 f_size;
|
|
u32 f_unknown20;
|
|
u32 f_unknown24;
|
|
u32 f_unknown28;
|
|
u32 f_unknown2C;
|
|
|
|
ctx_set("_peg_frame_idx", frame_idx);
|
|
ctx_set("_peg_frame_offset", f_data_offset);
|
|
ctx_set("_peg_frame_width", f_width);
|
|
ctx_set("_peg_frame_height", f_height);
|
|
ctx_set("_peg_frame_format", f_format);
|
|
ctx_set("_peg_frame_size", f_size);
|
|
ctx_set("_peg_frame_flags", f_flags);
|
|
|
|
// Determine format name
|
|
if (f_format == 400) { format_name = "DXT1"; }
|
|
else if (f_format == 401) { format_name = "DXT3"; }
|
|
else if (f_format == 402) { format_name = "DXT5"; }
|
|
else if (f_format == 403) { format_name = "R5G6B5"; }
|
|
else if (f_format == 404) { format_name = "A1R5G5B5"; }
|
|
else if (f_format == 405) { format_name = "A4R4G4B4"; }
|
|
else if (f_format == 406) { format_name = "R8G8B8"; }
|
|
else if (f_format == 407) { format_name = "A8R8G8B8"; }
|
|
else if (f_format == 408) { format_name = "V8U8"; }
|
|
else if (f_format == 409) { format_name = "CxV8U8"; }
|
|
else if (f_format == 410) { format_name = "A8"; }
|
|
else if (f_format == 603) { format_name = "Unknown_603"; }
|
|
else { format_name = "Unknown"; }
|
|
ctx_set("_peg_frame_format_name", format_name);
|
|
|
|
dummy = bytesat(0, 1);
|
|
frame_child = dummy |> parse volition_peg_frame_row;
|
|
push("frames", frame_child);
|
|
|
|
frame_idx = frame_idx + 1;
|
|
}
|
|
|
|
// Parse texture names (null-terminated strings)
|
|
name_idx = 0;
|
|
repeat(texture_count) {
|
|
tex_name = cstring();
|
|
ctx_set("_peg_tex_name_" + name_idx, tex_name);
|
|
name_idx = name_idx + 1;
|
|
}
|
|
|
|
ui_table("frames", "frames", "Index,Width,Height,Format,Size,Flags");
|
|
|
|
total_frames = frame_count ui("Total Frames");
|
|
total_textures = texture_count ui("Total Textures");
|
|
file_size = size() ui("File Size");
|
|
}
|
|
|
|
// Frame row for table display
|
|
type volition_peg_frame_row ui("Frame") byteorder LE
|
|
{
|
|
set_hidden();
|
|
Index = ctx_get("_peg_frame_idx");
|
|
Width = ctx_get("_peg_frame_width");
|
|
Height = ctx_get("_peg_frame_height");
|
|
Format = ctx_get("_peg_frame_format_name");
|
|
Size = ctx_get("_peg_frame_size");
|
|
Flags = ctx_get("_peg_frame_flags");
|
|
}
|