// String Table format - THQ Australia Engine // Magic: "bats" // Avatar/Wii: 12-byte entries, ASCII strings // SpongeBob/Xbox: 16-byte entries, UTF-16LE strings // Avatar-style string table (12-byte entries, ASCII) type thqa_str [root, display="String Table"] byteorder LE { criteria { require ascii(bytesat(0, 4)) == "bats"; } magic = ascii(read(4)); ui("magic", "Magic"); u32 language_code; ui("language_code", "Language Code"); u32 entry_count; ui("entry_count", "Entry Count"); // Entries are 12 bytes each entries_end = 12 + (entry_count * 12); string_data_start = entries_end + 8; // First pass: collect all string offsets idx = 0; repeat(entry_count) { u16 e_string_id; u16 e_lang; u32 e_string_offset; u32 e_reference; ctx_set("_thqa_str_entry_" + idx + "_id", e_string_id); ctx_set("_thqa_str_entry_" + idx + "_lang", e_lang); ctx_set("_thqa_str_entry_" + idx + "_offset", e_string_offset); ctx_set("_thqa_str_entry_" + idx + "_ref", e_reference); idx = idx + 1; } // Skip srts marker and size skip(8); // Second pass: read strings and create table rows idx = 0; repeat(entry_count) { e_string_id = ctx_get("_thqa_str_entry_" + idx + "_id"); e_lang = ctx_get("_thqa_str_entry_" + idx + "_lang"); e_string_offset = ctx_get("_thqa_str_entry_" + idx + "_offset"); e_reference = ctx_get("_thqa_str_entry_" + idx + "_ref"); seek(string_data_start + e_string_offset); str_value = cstring(); // Store values for child type ctx_set("_thqa_str_value", str_value); ctx_set("_thqa_str_id", e_string_id); ctx_set("_thqa_str_lang", e_lang); ctx_set("_thqa_str_ref", e_reference); // Create row via child type (fields become table columns) dummy_byte = bytesat(0, 1); entry_child = dummy_byte |> parse thqa_str_row; push("strings", entry_child); idx = idx + 1; } // Table display with columns matching child type field names // skip_tree() prevents tree children (table-only display) skip_tree("strings"); ui_table("strings", "Strings", "ID,Lang,Ref,Value"); seek(entries_end); srts_marker = ascii(read(4)); ui("srts_marker", "String Marker"); u32 string_data_size; ui("string_data_size", "String Data Size"); total_strings = entry_count; ui("total_strings", "Total Strings"); file_size = size(); ui("file_size", "File Size"); } // SpongeBob/Xbox-style string table (16-byte entries, UTF-16LE) type thqa_str_le [root, display="String Table"] byteorder LE { criteria { require ascii(bytesat(0, 4)) == "bats"; } magic = ascii(read(4)); ui("magic", "Magic"); u32 version; ui("version", "Version"); u32 entry_count; ui("entry_count", "Entry Count"); // Entries are 16 bytes each entries_end = 12 + (entry_count * 16); string_data_start = entries_end + 8; // First pass: collect all entry data idx = 0; repeat(entry_count) { u16 e_string_id; u16 e_type; u32 e_string_offset; u32 e_unknown; f32 e_float_val; ctx_set("_thqa_str_le_" + idx + "_id", e_string_id); ctx_set("_thqa_str_le_" + idx + "_type", e_type); ctx_set("_thqa_str_le_" + idx + "_offset", e_string_offset); ctx_set("_thqa_str_le_" + idx + "_float", e_float_val); idx = idx + 1; } // Skip srts marker and size skip(8); // Second pass: read UTF-16LE strings and create table rows idx = 0; repeat(entry_count) { e_string_id = ctx_get("_thqa_str_le_" + idx + "_id"); e_type = ctx_get("_thqa_str_le_" + idx + "_type"); e_string_offset = ctx_get("_thqa_str_le_" + idx + "_offset"); e_float_val = ctx_get("_thqa_str_le_" + idx + "_float"); seek(string_data_start + e_string_offset); str_value = wstring(); // Store values for child type ctx_set("_thqa_str_value", str_value); ctx_set("_thqa_str_id", e_string_id); ctx_set("_thqa_str_type", e_type); // Create row via child type (fields become table columns) dummy_byte = bytesat(0, 1); entry_child = dummy_byte |> parse thqa_str_le_row; push("strings", entry_child); idx = idx + 1; } // Table display with columns matching child type field names // skip_tree() prevents tree children (table-only display) skip_tree("strings"); ui_table("strings", "Strings", "ID,Type,Value"); seek(entries_end); srts_marker = ascii(read(4)); ui("srts_marker", "String Marker"); u32 string_data_size; ui("string_data_size", "String Data Size"); total_strings = entry_count; ui("total_strings", "Total Strings"); file_size = size(); ui("file_size", "File Size"); } // Row type for Avatar string table - field names match column names // set_hidden() prevents tree children while keeping table data type thqa_str_row [display="String"] byteorder LE { set_hidden(); ID = ctx_get("_thqa_str_id"); Lang = ctx_get("_thqa_str_lang"); Ref = ctx_get("_thqa_str_ref"); // Replace Cyrillic button placeholders with readable Xbox button names Value = replace_xbox_buttons(ctx_get("_thqa_str_value")); } // Row type for SpongeBob string table - field names match column names type thqa_str_le_row [display="String"] byteorder LE { set_hidden(); ID = ctx_get("_thqa_str_id"); Type = ctx_get("_thqa_str_type"); // Replace Cyrillic button placeholders with readable Xbox button names Value = replace_xbox_buttons(ctx_get("_thqa_str_value")); }