diff --git a/README.md b/README.md index 1090355..bec62b8 100644 --- a/README.md +++ b/README.md @@ -58,6 +58,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | GIF | `image/gif` | [`patterns/gif.hexpat`](patterns/gif.hexpat) | GIF image files | | GZIP | `application/gzip` | [`patterns/gzip.hexpat`](patterns/gzip.hexpat) | GZip compressed data format | | Halo Bitmap || [`patterns/hinf_bitmap.hexpat`](patterns/hinf_bitmap.hexpat) | Halo Infinite Bitmap tag files | +| Halo HavokScript || [`patterns/hinf_luas.hexpat`](patterns/hinf_luas.hexpat) | Halo Infinite HavokScript 5.1 Bytecode | | ICO | | [`patterns/ico.hexpat`](patterns/ico.hexpat) | Icon (.ico) or Cursor (.cur) files | | ID3 | `audio/mpeg` | [`patterns/id3.hexpat`](patterns/id3.hexpat) | ID3 tags in MP3 files | | Intel HEX | | [`patterns/intel_hex.hexpat`](patterns/intel_hex.hexpat) | [Intel hexadecimal object file format definition]("https://en.wikipedia.org/wiki/Intel_HEX") | diff --git a/patterns/hinf_luas.hexpat b/patterns/hinf_luas.hexpat new file mode 100644 index 0000000..802e4df --- /dev/null +++ b/patterns/hinf_luas.hexpat @@ -0,0 +1,441 @@ +#pragma author Surasia +#pragma description Halo Infinite HavokScript 5.1 "luas" file + +#include +#include +#include +#include + +struct String32 { + s32 length; + if (length != 0) { + char s[length - 1]; + u8 endbyte; + } +}; + +struct String64 { + s64 length; + if (length != 0) { + char s[length - 1]; + u8 endbyte; + } +}; + +enum lua_endian : u8 { + little_endian = 1, + big_endian = 0, +}; + +enum lua_numbertype : u8 { + _float = 0, + _int32 = 1, +}; + +struct lua_enum { + s32 value; + String32 string; +}; + +struct LuaEnum { + s32 count; + lua_enum entries[count]; + }; + +enum HksOpCode : s8 { + GETFIELD, + TEST, + CALL_I, + CALL_C, + EQ, + EQ_BK, + GETGLOBAL, + MOVE, + SELF, + RETURN, + GETTABLE_S, + GETTABLE_N, + GETTABLE, + LOADBOOL, + TFORLOOP, + SETFIELD, + SETTABLE_S, + SETTABLE_S_BK, + SETTABLE_N, + SETTABLE_N_BK, + SETTABLE, + SETTABLE_BK, + TAILCALL_I, + TAILCALL_C, + TAILCALL_M, + LOADK, + LOADNIL, + SETGLOBAL, + JMP, + CALL_M, + CALL, + INTRINSIC_INDEX, + INTRINSIC_NEWINDEX, + INTRINSIC_SELF, + INTRINSIC_LITERAL, + INTRINSIC_NEWINDEX_LITERAL, + INTRINSIC_SELF_LITERAL, + TAILCALL, + GETUPVAL, + SETUPVAL, + ADD, + ADD_BK, + SUB, + SUB_BK, + MUL, + MUL_BK, + DIV, + DIV_BK, + MOD, + MOD_BK, + POW, + POW_BK, + NEWTABLE, + UNM, + NOT, + LEN, + LT, + LT_BK, + LE, + LE_BK, + CONCAT, + TESTSET, + FORPREP, + FORLOOP, + SETLIST, + CLOSE, + CLOSURE, + VARARG, + TAILCALL_I_R1, + CALL_I_R1, + SETUPVAL_R1, + TEST_R1, + NOT_R1, + GETFIELD_R1, + SETFIELD_R1, + NEWSTRUCT, + DATA, + SETSLOTN, + SETSLOTI, + SETSLOT, + SETSLOTS, + SETSLOTMT, + CHECKTYPE, + CHECKTYPES, + GETSLOT, + GETSLOTMT, + SELFSLOT, + SELFSLOTMT, + GETFIELD_MM, + CHECKTYPE_D, + GETSLOT_D, + GETGLOBAL_MEM, + }; + +enum HksType : u8 + { + TNIL, + TBOOLEAN, + TLIGHTUSERDATA, + TNUMBER, + TSTRING, + TTABLE, + TFUNCTION, + TUSERDATA, + TTHREAD, + TIFUNCTION, + TCFUNCTION, + TUI64, + TSTRUCT + }; + +bitfield REG_CONST_UNUSED { + argB : 17 & 0x1ffff; + argA : 8 & 0xff; +}; + +bitfield REG_NUM_NUM { + argC : 9 & 0x1ff; + argB : 8 & 0xff; + argA : 8 & 0xff; +}; + +bitfield REG_NUM_UNUSED { + argB : 8 & 0xff; + argA : 8 & 0xff; + argC : 9 & 0x1ff; +}; + +bitfield UNUSED_OFFSET_UNUSED { + argA : 8 & 0xff; + argB : 9 & 0x1ff; + argC : 8 & 0xff; +}; + + +bitfield LuaBitfield { + HksOpCode OpCode : 7; + //Arguments : 25; // read on how to process them at https://raw.githubusercontent.com/soupstream/havok-script-tools/main/HavokScriptToolsCommon/HksOpCodes.cs + match(OpCode) { + (HksOpCode::LOADK) : REG_CONST_UNUSED arguments; + (HksOpCode::SETGLOBAL) : REG_CONST_UNUSED arguments; + (HksOpCode::NEWTABLE) : REG_NUM_NUM arguments; + (HksOpCode::CLOSURE) : REG_CONST_UNUSED arguments; + (HksOpCode::RETURN) : REG_NUM_UNUSED arguments; + (HksOpCode::NEWSTRUCT) : REG_NUM_NUM arguments; + (HksOpCode::DATA) : UNUSED_OFFSET_UNUSED arguments; + (_): arguments : 25; + } +}; + +struct LuaInstruction { + LuaBitfield OpCode; +}; + +struct LuaConstant { + HksType type; + if (type == HksType::TNIL) { + } + else if (type == HksType::TSTRING) { + String64 string; + } + else if (type == HksType::TNUMBER) { + float number; + } + else if (type == HksType::TBOOLEAN) { + bool boolean; + } + else if (type == HksType::TLIGHTUSERDATA) { + s64 userdata; + } + else { + s64 data; + } +}; + +struct HksLocal { + String64 string; + u32 start; + u32 end; +}; + + +struct LuaFunction { + + s32 upvaluecount; + s32 paramcount; + s8 isVarArg; + s32 slotCount; + s32 unk; + s32 instruction_count; + if ($ % 4 != 0) { + $ += (4 - ($ % 4)); + } + LuaInstruction instructions[instruction_count]; + s32 constantCount; + LuaConstant constants[constantCount];; + s32 HasDebugInfo; + if (HasDebugInfo != 0) { + u32 LineCount; + u32 LocalsCount; + u32 UpValueCount; + u32 LineBegin; + u32 LineEnd; + String64 path; + String64 functionName; + s32 Lines[LineCount]; + HksLocal Locals[LocalsCount]; + String64 UpValues[UpValueCount]; + } + s32 function_count; + LuaFunction children[function_count]; +}; + + + +struct LuaStructHeader { + String64 name; + s32 structID; + s32 pad; + s24 unk1; + HksType type; + s32 unk2; +}; + + +struct LuaStructMember { + s32 pad; + LuaStructHeader header; + s32 index; +}; + +struct LuaStruct { + try { + u32 unk; + LuaStructHeader ExtendedStructHeader; + s64 ExtendedStructMemberCount; + LuaStructMember ExtendedStructMembers[ExtendedStructMemberCount]; + u32 unk2; + } catch { + std::print("WARNING: this file does not support structs."); + } + try { + LuaStructHeader StructHeader; + s64 StructMemberCount; + LuaStructMember StructMembers[StructMemberCount]; + } catch { + std::print("WARNING: this file does not support multiple structs."); + } +}; + + +struct LuaHeader { + char luaheader[4]; + u8 version; + u8 format; + lua_endian Endianness; + if (Endianness == lua_endian::big_endian) { + std::core::set_endian(std::mem::Endian::Big); + } else { + std::core::set_endian(std::mem::Endian::Little); + } + u8 intSize; + u8 size_tSize; + u8 instruction_size; + u8 number_size; + lua_numbertype numberType; + u8 flags; + u8 unknown; + LuaEnum enums; + LuaFunction LuaFunctions; + LuaStruct LuaStructures; +}; + +struct Header { + u32 magic; + u32 version; + u64 RootStructAltGUID; + u64 AssetChecksum; + u32 DependencyCount; + s32 DataBlockCount; + s32 TagStructCount; + s32 DataReferenceCount; + s32 TagReferenceCount; + u32 StringTableSize; + u32 ZoneSetDataSize; + u32 Unk_0x34; + u32 HeaderSize; + u32 DataSize; + u32 ResourceDataSize; + u32 ActualResoureDataSize; + u8 HeaderAlignment; + u8 TagDataAlignment; + u8 ResourceDataAlignment; + u8 ActualResourceDataAlignment; + s32 IsResource; +}; + +struct _s_tagblock { + char tagblock[16]; + s32 count; +}; + +struct _s_resource { + char resource[12]; + s32 count; +}; + +struct _s_data { + char data[24]; +}; + + +struct Data_Block { + u32 Size; + u16 Padding; + u16 Section; + u64 Offset; +}; + +struct Tag_Def_Structure { + s64 GUID_1; + s64 GUID_2; + u16 Type; + u16 Unk_0x12; + s32 TargetIndex; + s32 FieldBlock; + u32 FieldOffset; +}; + +struct Tag_Dependency { + char GroupTag[4] [[format("std::string::reverse")]]; + u32 NameOffset; + u64 AssetID; + u32 GlobalID; + u32 Unk_0x14; +}; + +struct Data_Reference { + s32 ParentStructIndex; + s32 Unk_0x04; + s32 TargetIndex; + s32 FieldBlock; + u32 FieldOffset; +}; + +struct _s_tagref { + s64 GlobalHandle; + s32 ref_id_int; + s32 ref_id_sub; + s32 ref_id_center_int; + char TagGroupRev[4] [[format("std::string::reverse")]]; + s32 local_handle; +}; + +struct Tag_Fixup_Reference { + s32 FieldBlock; + u32 FieldOffset; + u32 NameOffset; + s32 DependencyIndex; + _s_tagref tagreference @ header.HeaderSize + datablocks[FieldBlock].Offset + FieldOffset; +}; + +struct InternalStruct { + s64 vtablespace; + u32 global_tag_id; + char local_tag_handle[4]; +}; + +struct LuaReferencedTagContainer { + _s_tagref tagReference; +}; +struct LuaScriptTagDefinition { + u32 luaFileName; + _s_data luaFileData; + char luaFileNameString[256]; + _s_tagblock referencedTags_count; + LuaHeader lua_header; + try { + char pad[14]; + LuaReferencedTagContainer referencedTags[referencedTags_count.count]; + } catch { + std::print("This file does not support referenced Tags."); + } +}; + + +// TAG LAYOUT +Header header @ 0x00; +Tag_Dependency Dependencies[header.DependencyCount] @ 0x50; +Data_Block datablocks[header.DataBlockCount] @ header.DependencyCount * 0x18 + 0x50; +Tag_Def_Structure tagdefstructure[header.TagStructCount] @ header.DependencyCount * 0x18 + 0x50 + header.DataBlockCount * 0x10; +Data_Reference Data_References[header.DataReferenceCount] @ header.DependencyCount * 0x18 + 0x50 + header.DataBlockCount * 0x10 + header.TagStructCount * 0x20; +Tag_Fixup_Reference tagfixupreference[header.TagReferenceCount] @ header.DependencyCount * 0x18 + 0x50 + header.DataBlockCount * 0x10 + header.TagStructCount * 0x20 + header.DataReferenceCount * 0x14; +char ZoneSet[header.ZoneSetDataSize] @ header.HeaderSize - header.ZoneSetDataSize; +InternalStruct internalstruct @ header.HeaderSize; +LuaScriptTagDefinition lua_script @ header.HeaderSize + 16; \ No newline at end of file diff --git a/tests/patterns/test_data/hinf_luas.hexpat.luas b/tests/patterns/test_data/hinf_luas.hexpat.luas new file mode 100644 index 0000000..2a508d1 Binary files /dev/null and b/tests/patterns/test_data/hinf_luas.hexpat.luas differ