XPlor/definitions/asura/asura_archive.xscript
njohnson d0510d7ab2 Update Asura archive XScript definitions
- Enhanced chunk parsing
- Improved structure definitions

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-08 00:38:33 -05:00

166 lines
6.5 KiB
Plaintext

// Asura Engine archive format
// Used by Rebellion games: Sniper Elite series, AVP 2010, etc.
// Archives contain chunks like FCSR (resources), TSXT (textures), LFSR (lists), etc.
type asura_archive [display="Asura Archive"] byteorder BE
{
criteria {
// Match "Asura " signature (8 bytes with trailing spaces)
require ascii(bytesat(0, 8)) == "Asura ";
}
// Store base name for chunk naming (use full filename, not basename)
base_name = _name;
// Set display name for this archive
_name = base_name + ".asr";
// Asura signature
signature = ascii(read(8));
signature = signature [ui, readonly, display="Signature"];
// Track chunk indices (separate counter per type for naming)
chunk_index = 0;
set_global("_fcsr_idx", 0);
set_global("_tsxt_idx", 0);
set_global("_lfsr_idx", 0);
set_global("_stsa_idx", 0);
set_global("_txet_idx", 0);
set_global("_gsms_idx", 0);
set_global("_itne_idx", 0);
set_global("_stuc_idx", 0);
set_global("_catc_idx", 0);
set_global("_rttc_idx", 0);
set_global("_vetc_idx", 0);
set_global("_pose_idx", 0);
set_global("_pvet_idx", 0);
set_global("_tatc_idx", 0);
set_global("_ttat_idx", 0);
set_global("_tvet_idx", 0);
set_global("_ucat_idx", 0);
set_global("_uvet_idx", 0);
set_global("_unk_idx", 0);
// Parse chunks until end of file (need at least 16 bytes for header)
while (pos() + 16 <= size()) {
// Save chunk start position
chunk_start = pos();
// Peek at chunk header to get ID and size
peek_id = ascii(read(4));
u32 peek_size;
// Seek back to chunk start for proper parsing
seek(chunk_start);
// Validate chunk - if peek_size is 0 or invalid, stop parsing
if (peek_size == 0 || peek_size > size() - chunk_start) {
remaining = size() - pos();
if (remaining > 0) {
raw_tail = read(remaining);
raw_tail = raw_tail [ui, display="Raw Tail Data"];
}
break;
}
// Set chunk name before parsing (child will inherit this)
_name = base_name + ".chunk" + chunk_index;
// Dispatch to appropriate chunk type based on ID and push to arrays
// Note: increment index AFTER parse so chunk can read current value
if (peek_id == "FCSR" || peek_id == "RSCF") {
chunk = parse_here("fcsr_chunk");
push("fcsr_chunks", chunk);
set_global("_fcsr_idx", get_global("_fcsr_idx") + 1);
} else if (peek_id == "TSXT" || peek_id == "TXST") {
chunk = parse_here("tsxt_chunk");
push("tsxt_chunks", chunk);
set_global("_tsxt_idx", get_global("_tsxt_idx") + 1);
} else if (peek_id == "LFSR" || peek_id == "RSFL") {
chunk = parse_here("lfsr_chunk");
push("lfsr_chunks", chunk);
set_global("_lfsr_idx", get_global("_lfsr_idx") + 1);
} else if (peek_id == "STSA" || peek_id == "ASTS") {
chunk = parse_here("stsa_chunk");
push("stsa_chunks", chunk);
set_global("_stsa_idx", get_global("_stsa_idx") + 1);
} else if (peek_id == "TXET" || peek_id == "TEXT") {
chunk = parse_here("txet_chunk");
push("txet_chunks", chunk);
set_global("_txet_idx", get_global("_txet_idx") + 1);
} else if (peek_id == "GSMS" || peek_id == "SMSG") {
chunk = parse_here("gsms_chunk");
push("gsms_chunks", chunk);
set_global("_gsms_idx", get_global("_gsms_idx") + 1);
} else if (peek_id == "ITNE" || peek_id == "ENTI") {
chunk = parse_here("itne_chunk");
push("itne_chunks", chunk);
set_global("_itne_idx", get_global("_itne_idx") + 1);
} else if (peek_id == "STUC" || peek_id == "CUTS") {
chunk = parse_here("stuc_chunk");
push("stuc_chunks", chunk);
set_global("_stuc_idx", get_global("_stuc_idx") + 1);
} else if (peek_id == "CATC") {
chunk = parse_here("catc_chunk");
push("catc_chunks", chunk);
set_global("_catc_idx", get_global("_catc_idx") + 1);
} else if (peek_id == "RTTC") {
chunk = parse_here("rttc_chunk");
push("rttc_chunks", chunk);
set_global("_rttc_idx", get_global("_rttc_idx") + 1);
} else if (peek_id == "VETC") {
chunk = parse_here("vetc_chunk");
push("vetc_chunks", chunk);
set_global("_vetc_idx", get_global("_vetc_idx") + 1);
} else if (peek_id == "POSE") {
chunk = parse_here("pose_chunk");
push("pose_chunks", chunk);
set_global("_pose_idx", get_global("_pose_idx") + 1);
} else if (peek_id == "PVET") {
chunk = parse_here("pvet_chunk");
push("pvet_chunks", chunk);
set_global("_pvet_idx", get_global("_pvet_idx") + 1);
} else if (peek_id == "TATC") {
chunk = parse_here("tatc_chunk");
push("tatc_chunks", chunk);
set_global("_tatc_idx", get_global("_tatc_idx") + 1);
} else if (peek_id == "TTAT") {
chunk = parse_here("ttat_chunk");
push("ttat_chunks", chunk);
set_global("_ttat_idx", get_global("_ttat_idx") + 1);
} else if (peek_id == "TVET") {
chunk = parse_here("tvet_chunk");
push("tvet_chunks", chunk);
set_global("_tvet_idx", get_global("_tvet_idx") + 1);
} else if (peek_id == "UCAT") {
chunk = parse_here("ucat_chunk");
push("ucat_chunks", chunk);
set_global("_ucat_idx", get_global("_ucat_idx") + 1);
} else if (peek_id == "UVET") {
chunk = parse_here("uvet_chunk");
push("uvet_chunks", chunk);
set_global("_uvet_idx", get_global("_uvet_idx") + 1);
} else {
// Unknown chunk type - parse as generic chunk
chunk = parse_here("asura_chunk");
push("unknown_chunks", chunk);
set_global("_unk_idx", get_global("_unk_idx") + 1);
}
// Increment chunk index
chunk_index = chunk_index + 1;
// Seek to next chunk (chunk_start + peek_size)
next_chunk = chunk_start + peek_size;
if (next_chunk > size()) {
break;
}
seek(next_chunk);
}
// Restore archive name and show total count
_name = base_name + ".asr";
chunk_count = chunk_index;
chunk_count = chunk_count [ui, readonly, display="Total Chunks"];
}