XPlor/definitions/volition/peg.xscript

208 lines
6.3 KiB
Plaintext
Raw Normal View History

// 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");
}