diff --git a/pkNX.Structures/Scripts/AmxError.cs b/pkNX.Structures/Scripts/AmxError.cs new file mode 100644 index 00000000..d95b24eb --- /dev/null +++ b/pkNX.Structures/Scripts/AmxError.cs @@ -0,0 +1,35 @@ +namespace pkNX.Structures +{ + public enum AmxError + { + AMX_ERR_NONE, + /* reserve the first 15 error codes for exit codes of the abstract machine */ + EXIT, /* forced exit */ + ASSERT, /* assertion failed */ + STACKERR, /* stack/heap collision */ + BOUNDS, /* index out of bounds */ + MEMACCESS, /* invalid memory access */ + INVINSTR, /* invalid instruction */ + STACKLOW, /* stack underflow */ + HEAPLOW, /* heap underflow */ + CALLBACK, /* no callback, or invalid callback */ + NATIVE, /* native function failed */ + DIVIDE, /* divide by zero */ + SLEEP, /* go into sleepmode - code can be restarted */ + INVSTATE, /* no implementation for this state, no fall-back */ + + MEMORY = 16, /* out of memory */ + FORMAT, /* invalid file format */ + VERSION, /* file is for a newer version of the AMX */ + NOTFOUND, /* function not found */ + INDEX, /* invalid index parameter (bad entry point) */ + DEBUG, /* debugger cannot run */ + INIT, /* AMX not initialized (or doubly initialized) */ + USERDATA, /* unable to set user data field (table full) */ + INIT_JIT, /* cannot initialize the JIT */ + PARAMS, /* parameter error */ + DOMAIN, /* domain error, expression result does not fit in range */ + GENERAL, /* general error (unknown or unspecific error) */ + OVERLAY, /* overlays are unsupported (JIT) or uninitialized */ + } +} \ No newline at end of file diff --git a/pkNX.Structures/Scripts/AmxFlags.cs b/pkNX.Structures/Scripts/AmxFlags.cs new file mode 100644 index 00000000..5a2134ad --- /dev/null +++ b/pkNX.Structures/Scripts/AmxFlags.cs @@ -0,0 +1,57 @@ +using System; + +namespace pkNX.Structures +{ + /// + /// Feature flags for the + /// + /// + /// Flag version listed here is for CUR_FILE_VERSION = 10, circa 2008 + /// + [Flags] + public enum AmxFlags : short + { + NONE, + + /// All function calls use overlays + OVERLAY = 0x01, + + /// Symbolic info is available + DEBUG = 0x02, + + /// Compact encoding + COMPACT = 0x04, + + /// Script uses the sleep instruction (possible re-entry or power-down mode) + SLEEP = 0x08, + + /// No array bounds checking; no BREAK opcodes + NOCHECKS = 0x10, + + /// Data section is explicitly initialized + DSEG_INIT = 0x20, + + /// Script new (optimized) version of SYSREQ opcode + SYSREQN = 0x800, + + /// All native functions are registered + NTVREG = 0x1000, + + /// Abstract machine is JIT compiled + JITC = 0x2000, + + /// Busy verifying P-code + VERIFY = 0x4000, + + /// AMX has been initialized + INIT = 8000 + } + + public static class AmxFlagsExtensions + { + public static bool HasFlagFast(this AmxFlags value, AmxFlags flag) + { + return (value & flag) != 0; + } + } +} \ No newline at end of file diff --git a/pkNX.Structures/Scripts/AmxHeader.cs b/pkNX.Structures/Scripts/AmxHeader.cs new file mode 100644 index 00000000..62131459 --- /dev/null +++ b/pkNX.Structures/Scripts/AmxHeader.cs @@ -0,0 +1,82 @@ +using System.Runtime.InteropServices; + +namespace pkNX.Structures +{ + [StructLayout(LayoutKind.Sequential)] + public class AmxHeader + { + // Cell Size magic verification + public const ushort MAGIC_32 = 0xf1e0; + public const ushort MAGIC_64 = 0xf1e1; + public const ushort MAGIC_16 = 0xf1e2; + + /// Size of the "file" + public int Size; // 0x00 + + /// Signature + public ushort Magic; // 0x04 + + /// File format version + public byte FileVersion; // 0x06 + + /// Required version of the AMX + public byte AMXVersion; // 0x07 + + /// Feature flags + public AmxFlags Flags; // 0x08 + + /// Size of a definition record + public short DefinitionSize; // 0x0A + + /// Initial value of COD - code block + public int COD; // 0x0C + + /// initial value of DAT - data block + public int Data; // 0x10 + + /// Initial value of HEA - start of the heap + public int Heap; // 0x14 + + /// Initial value of STP - stack top + public int StackTop; // 0x18 + + /// Initial value of CIP - the instruction pointer + public int CurrentInstructionPointer; // 0x20 + + /// Offset to the "public functions" table + public int Publics; // 0x24 + + /// Offset to the "native functions" table + public int Natives; // 0x28 + + /// Offset to the table of libraries + public int Libraries; // 0x2C + + /// Offset to the "public variables" table + public int PublicVars; // 0x30 + + /// Offset to the "public tagnames" table + public int Tags; // 0x34 + + /// Offset to the name table + public int NameTable; // 0x38 + + /// Offset to the overlay table + public int Overlays; // 0x3C + } + +/* File format version + * 0 original version + * 1 opcodes JUMP.pri, SWITCH and CASETBL + * 2 compressed files + * 3 public variables + * 4 opcodes SWAP.pri/alt and PUSHADDR + * 5 tagnames table + * 6 reformatted header + * 7 name table, opcodes SYMTAG & SYSREQ.D + * 8 opcode BREAK, renewed debug interface + * 9 macro opcodes + * 10 position-independent code, overlays, packed instructions + * 11 relocating instructions for the native interface, reorganized instruction set + */ +} diff --git a/pkNX.Structures/Scripts/AmxOpCode.cs b/pkNX.Structures/Scripts/AmxOpCode.cs new file mode 100644 index 00000000..a320424e --- /dev/null +++ b/pkNX.Structures/Scripts/AmxOpCode.cs @@ -0,0 +1,226 @@ +namespace pkNX.Structures +{ + // https://github.com/gameswop/mtasa-resources/blob/d557a72fefef57ac34780a76edf16383d3dff0e8/%5Bgamemodes%5D/%5Bamx%5D/amx-deps/src/amx/amx.c#L143 + public enum AmxOpCode + { + NONE, /* invalid opcode */ + LOAD_PRI, + LOAD_ALT, + LOAD_S_PRI, + LOAD_S_ALT, + LREF_PRI, + LREF_ALT, + LREF_S_PRI, + LREF_S_ALT, + LOAD_I, + LODB_I, + CONST_PRI, + CONST_ALT, + ADDR_PRI, + ADDR_ALT, + STOR_PRI, + STOR_ALT, + STOR_S_PRI, + STOR_S_ALT, + SREF_PRI, + SREF_ALT, + SREF_S_PRI, + SREF_S_ALT, + STOR_I, + STRB_I, + LIDX, + LIDX_B, + IDXADDR, + IDXADDR_B, + ALIGN_PRI, + ALIGN_ALT, + LCTRL, + SCTRL, + MOVE_PRI, + MOVE_ALT, + XCHG, + PUSH_PRI, + PUSH_ALT, + PICK, + PUSH_C, + PUSH, + PUSH_S, + PPRI, + PALT, + STACK, + HEAP, + PROC, + RET, + RETN, + CALL, + CALL_PRI, + JUMP, + JREL, /* obsolete */ + JZER, + JNZ, + JEQ, + JNEQ, + JLESS, + JLEQ, + JGRTR, + JGEQ, + JSLESS, + JSLEQ, + JSGRTR, + JSGEQ, + SHL, + SHR, + SSHR, + SHL_C_PRI, + SHL_C_ALT, + SHR_C_PRI, + SHR_C_ALT, + SMUL, + SDIV, + SDIV_ALT, + UMUL, + UDIV, + UDIV_ALT, + ADD, + SUB, + SUB_ALT, + AND, + OR, + XOR, + NOT, + NEG, + INVERT, + ADD_C, + SMUL_C, + ZERO_PRI, + ZERO_ALT, + ZERO, + ZERO_S, + SIGN_PRI, + SIGN_ALT, + EQ, + NEQ, + LESS, + LEQ, + GRTR, + GEQ, + SLESS, + SLEQ, + SGRTR, + SGEQ, + EQ_C_PRI, + EQ_C_ALT, + INC_PRI, + INC_ALT, + INC, + INC_S, + INC_I, + DEC_PRI, + DEC_ALT, + DEC, + DEC_S, + DEC_I, + MOVS, + CMPS, + FILL, + HALT, + BOUNDS, + SYSREQ_PRI, + SYSREQ_C, + FILE, /* obsolete */ + LINE, /* obsolete */ + SYMBOL, /* obsolete */ + SRANGE, /* obsolete */ + JUMP_PRI, + SWITCH, + CASETBL, + SWAP_PRI, + SWAP_ALT, + PUSH_ADR, + NOP, + SYSREQ_N, + SYMTAG, /* obsolete */ + BREAK, /* macro instructions */ + PUSH2_C, + PUSH2, + PUSH2_S, + PUSH2_ADR, + PUSH3_C, + PUSH3, + PUSH3_S, + PUSH3_ADR, + PUSH4_C, + PUSH4, + PUSH4_S, + PUSH4_ADR, + PUSH5_C, + PUSH5, + PUSH5_S, + PUSH5_ADR, + LOAD_BOTH, + LOAD_S_BOTH, + CONST, + CONST_S, /* overlay instructions */ + ICALL, + IRETN, + ISWITCH, + ICASETBL, /* packed instructions */ +#if !AMX_NO_PACKED_OPC + LOAD_P_PRI, + LOAD_P_ALT, + LOAD_P_S_PRI, + LOAD_P_S_ALT, + LREF_P_PRI, + LREF_P_ALT, + LREF_P_S_PRI, + LREF_P_S_ALT, + LODB_P_I, + CONST_P_PRI, + CONST_P_ALT, + ADDR_P_PRI, + ADDR_P_ALT, + STOR_P_PRI, + STOR_P_ALT, + STOR_P_S_PRI, + STOR_P_S_ALT, + SREF_P_PRI, + SREF_P_ALT, + SREF_P_S_PRI, + SREF_P_S_ALT, + STRB_P_I, + LIDX_P_B, + IDXADDR_P_B, + ALIGN_P_PRI, + ALIGN_P_ALT, + PUSH_P_C, + PUSH_P, + PUSH_P_S, + STACK_P, + HEAP_P, + SHL_P_C_PRI, + SHL_P_C_ALT, + SHR_P_C_PRI, + SHR_P_C_ALT, + ADD_P_C, + SMUL_P_C, + ZERO_P, + ZERO_P_S, + EQ_P_C_PRI, + EQ_P_C_ALT, + INC_P, + INC_P_S, + DEC_P, + DEC_P_S, + MOVS_P, + CMPS_P, + FILL_P, + HALT_P, + BOUNDS_P, + PUSH_P_ADR, +#endif + /* ----- */ + SYSREQ_D, + SYSREQ_ND, /* ----- */ + NUM_OPCODES + } +} diff --git a/pkNX.Structures/Scripts/Script.cs b/pkNX.Structures/Scripts/Script.cs index 0035fe6d..3cb9485c 100644 --- a/pkNX.Structures/Scripts/Script.cs +++ b/pkNX.Structures/Scripts/Script.cs @@ -9,11 +9,12 @@ namespace pkNX.Structures /// https://github.com/compuphase/pawn public class Script { - public int Length => BitConverter.ToInt32(Raw, 0x00); - public uint Magic => BitConverter.ToUInt32(Raw, 0x04); + public AmxHeader Header; + public int Length => Header.Size; + public uint Magic => Header.Magic; // case 0x0A0AF1E0: code = read_code_block(f); break; // case 0x0A0AF1EF: debug = read_debug_block(f); break; - public bool Debug => Magic == 0x0A0AF1EF; + public bool Debug => Header.Flags.HasFlagFast(AmxFlags.DEBUG); public ushort PtrOffset => BitConverter.ToUInt16(Raw, 0x08); public ushort PtrCount => BitConverter.ToUInt16(Raw, 0x0A); @@ -48,9 +49,10 @@ public class Script public Script(byte[] data = null) { Raw = data ?? Array.Empty(); + Header = data.ToClass(); // sub_51AAFC - if ((Raw[8] & 1) != 0) + if (Header.Flags.HasFlagFast(AmxFlags.OVERLAY)) throw new ArgumentException("Multi-environment script!?"); }