mirror of
https://github.com/kwsch/pkNX.git
synced 2026-04-25 23:36:55 -05:00
Improved AMX decompilation and fixed recompilation to produce the same binary as the input file (#14)
This commit is contained in:
parent
f058d25773
commit
44c7052c69
|
|
@ -11,7 +11,9 @@ namespace pkNX.Structures
|
|||
/// </summary>
|
||||
/// <remarks>https://github.com/compuphase/pawn</remarks>
|
||||
public class Amx
|
||||
{
|
||||
{
|
||||
private const int MAX_NAME_LENGTH = 31;
|
||||
|
||||
public readonly byte[] Data;
|
||||
public readonly AmxHeader Header;
|
||||
public readonly int CellSize;
|
||||
|
|
@ -30,11 +32,19 @@ public Amx(byte[] data)
|
|||
|
||||
Unpack();
|
||||
|
||||
Debug.Assert(Header != null);
|
||||
Debug.Assert(Header.Magic != 0);
|
||||
Debug.Assert(Header.Natives <= Header.Libraries);
|
||||
Assert(Header != null);
|
||||
Assert(Header.Magic != 0);
|
||||
Assert(Header.Natives <= Header.Libraries);
|
||||
}
|
||||
|
||||
private void Assert( bool condition )
|
||||
{
|
||||
if ( !condition )
|
||||
{
|
||||
throw new Exception("Assertion failed");
|
||||
}
|
||||
}
|
||||
|
||||
public byte[] Write() => Data;
|
||||
public bool IsDebug => Header.Flags.HasFlagFast(AmxFlags.DEBUG);
|
||||
|
||||
|
|
@ -56,47 +66,50 @@ public IEnumerable<string> SummaryLines
|
|||
{
|
||||
get
|
||||
{
|
||||
yield return $"Code Start: 0x{Header.COD:X4}";
|
||||
yield return $"Data Start: 0x{Header.Data:X4}";
|
||||
yield return $"Total Used Size: 0x{Header.Heap:X4}";
|
||||
yield return $"Reserved Size: 0x{Header.StackTop:X4}";
|
||||
yield return $"Compressed Len: 0x{CompressedLength:X4}";
|
||||
yield return $"Decompressed Len: 0x{DecompressedLength:X4}";
|
||||
yield return $"Code Start: 0x{Header.COD:X4}";
|
||||
yield return $"Data Start: 0x{Header.Data:X4}";
|
||||
yield return $"Total Used Size: 0x{Header.Heap:X4}";
|
||||
yield return $"Reserved Size: 0x{Header.StackTop:X4}";
|
||||
yield return $"Compressed Len: 0x{CompressedLength:X4}";
|
||||
yield return $"Decompressed Len: 0x{DecompressedLength:X4}";
|
||||
yield return $"Entry Point: 0x{Header.CurrentInstructionPointer:X4}";
|
||||
yield return $"Compression Ratio: {(DecompressedLength - CompressedLength) / (decimal) DecompressedLength:p1}";
|
||||
}
|
||||
}
|
||||
|
||||
public Function LookupFunction(uint pc) => Array.Find(Functions, f => f.Within(pc));
|
||||
public Public LookupPublic(string name) => Array.Find(Publics, t => t.Name == name);
|
||||
public Public LookupPublic(uint addr) => Array.Find(Publics, t => t.Address == addr);
|
||||
public TableRecord LookupPublic(string name) => Array.Find(this.Publics, t => t.Name == name);
|
||||
public TableRecord LookupPublic(uint addr) => Array.Find(this.Publics, t => t.Address == addr);
|
||||
|
||||
public Function[] Functions { get; protected set; }
|
||||
public Public[] Publics { get; protected set; }
|
||||
public Variable[] Globals { get; protected set; }
|
||||
public Function[] Functions { get; protected set; }
|
||||
public TableRecord[] Publics { get; protected set; }
|
||||
public TableRecord[] Natives { get; protected set; }
|
||||
public TableRecord[] Libraries { get; protected set; }
|
||||
public TableRecord[] PublicVars { get; protected set; }
|
||||
public Variable[] Globals { get; protected set; }
|
||||
|
||||
public string ReadName(byte[] data, int offset)
|
||||
{
|
||||
var end = Array.FindIndex(data, offset, z => z == 0);
|
||||
if ( end < 0 )
|
||||
end = offset + MAX_NAME_LENGTH;
|
||||
if ( end >= data.Length )
|
||||
return null;
|
||||
return System.Text.Encoding.UTF8.GetString(data, offset, end - offset);
|
||||
}
|
||||
|
||||
public void Unpack()
|
||||
{
|
||||
if (Header.Publics > 0)
|
||||
{
|
||||
int count = (Header.Natives - Header.Publics) / Header.DefinitionSize;
|
||||
BinaryReader r = new BinaryReader(new MemoryStream(Data, Header.Publics, count * Header.DefinitionSize));
|
||||
Publics = new Public[count];
|
||||
for (int i = 0; i < Publics.Length; i++)
|
||||
{
|
||||
uint address = r.ReadUInt32();
|
||||
int nameoffset = r.ReadInt32();
|
||||
string name = ReadName(Data, nameoffset);
|
||||
Publics[i] = new Public(name, address);
|
||||
}
|
||||
}
|
||||
ReadPublics();
|
||||
if (Header.Natives > 0)
|
||||
ReadNatives();
|
||||
if (Header.Libraries > 0)
|
||||
ReadLibraries();
|
||||
if (Header.PublicVars > 0)
|
||||
ReadPublicVars();
|
||||
|
||||
if (IsDebug)
|
||||
if (IsDebug)
|
||||
{
|
||||
// todo
|
||||
}
|
||||
|
|
@ -107,305 +120,281 @@ public class Cell
|
|||
|
||||
}
|
||||
|
||||
protected void ReadPublics()
|
||||
{
|
||||
var count = (Header.Natives - Header.Publics) / Header.DefinitionSize;
|
||||
|
||||
Publics = ReadTable( Header.Publics, count );
|
||||
}
|
||||
|
||||
protected void ReadNatives()
|
||||
{
|
||||
var count = (Header.Libraries - Header.Natives) / Header.DefinitionSize;
|
||||
|
||||
Natives = ReadTable( Header.Natives, count );
|
||||
}
|
||||
|
||||
protected void ReadLibraries()
|
||||
{
|
||||
var count = (Header.PublicVars - Header.Libraries) / Header.DefinitionSize;
|
||||
|
||||
Libraries = ReadTable( Header.Libraries, count );
|
||||
}
|
||||
|
||||
protected void ReadPublicVars()
|
||||
{
|
||||
var count = (Header.Tags - Header.PublicVars) / Header.DefinitionSize;
|
||||
|
||||
PublicVars = ReadTable( Header.PublicVars, count );
|
||||
}
|
||||
|
||||
protected TableRecord[] ReadTable( int offset, int count )
|
||||
{
|
||||
using ( var stream = new MemoryStream( Data, offset, count * Header.DefinitionSize ) )
|
||||
using ( var reader = new BinaryReader( stream ) )
|
||||
{
|
||||
var dest = new TableRecord[ count ];
|
||||
|
||||
for ( int i = 0; i < dest.Length; i++ )
|
||||
{
|
||||
var address = reader.ReadUInt32();
|
||||
var nameoffset = reader.ReadUInt32();
|
||||
var name = default( string );
|
||||
|
||||
if ( nameoffset < Data.Length )
|
||||
name = ReadName( Data, (int) nameoffset );
|
||||
|
||||
name = name ?? "Unknown";
|
||||
dest[ i ] = new TableRecord( name, address );
|
||||
}
|
||||
|
||||
return dest;
|
||||
}
|
||||
}
|
||||
|
||||
private int sysreq_flg;
|
||||
|
||||
public void ParseOp(AmxOpCode op, ref int cip, ref Cell tgt)
|
||||
{
|
||||
void GETPARAM_P(Cell v, AmxOpCode o) { } // (v = ((Cell) (o) >> (int) (CellSize * 4)));}
|
||||
switch (op)
|
||||
{
|
||||
case AmxOpCode.PUSH5_C: /* instructions with 5 parameters */
|
||||
case AmxOpCode.PUSH5:
|
||||
case AmxOpCode.PUSH5_S:
|
||||
case AmxOpCode.PUSH5_ADR:
|
||||
cip += CellSize * 5;
|
||||
break;
|
||||
void GETPARAM_P( Cell v, AmxOpCode o ) { } // (v = ((Cell) (o) >> (int) (CellSize * 4)));}
|
||||
switch ( op )
|
||||
{
|
||||
case AmxOpCode.CONST:
|
||||
case AmxOpCode.CONST_S:
|
||||
cip += CellSize * 2;
|
||||
break;
|
||||
|
||||
case AmxOpCode.PUSH4_C: /* instructions with 4 parameters */
|
||||
case AmxOpCode.PUSH4:
|
||||
case AmxOpCode.PUSH4_S:
|
||||
case AmxOpCode.PUSH4_ADR:
|
||||
cip += CellSize * 4;
|
||||
break;
|
||||
/* Packed Instructions */
|
||||
case AmxOpCode.CONST_P_PRI:
|
||||
case AmxOpCode.CONST_P_ALT:
|
||||
case AmxOpCode.ADDR_P_PRI:
|
||||
case AmxOpCode.ADDR_P_ALT:
|
||||
case AmxOpCode.STRB_P_I:
|
||||
case AmxOpCode.LIDX_P_B:
|
||||
case AmxOpCode.IDXADDR_P_B:
|
||||
case AmxOpCode.ALIGN_P_PRI:
|
||||
case AmxOpCode.PUSH_P_C:
|
||||
case AmxOpCode.PUSH_P:
|
||||
case AmxOpCode.PUSH_P_S:
|
||||
case AmxOpCode.STACK_P:
|
||||
case AmxOpCode.HEAP_P:
|
||||
case AmxOpCode.SHL_P_C_PRI:
|
||||
case AmxOpCode.SHL_P_C_ALT:
|
||||
case AmxOpCode.ADD_P_C:
|
||||
case AmxOpCode.SMUL_P_C:
|
||||
case AmxOpCode.ZERO_P:
|
||||
case AmxOpCode.ZERO_P_S:
|
||||
case AmxOpCode.EQ_P_C_PRI:
|
||||
case AmxOpCode.EQ_P_C_ALT:
|
||||
case AmxOpCode.MOVS_P:
|
||||
case AmxOpCode.CMPS_P:
|
||||
case AmxOpCode.FILL_P:
|
||||
case AmxOpCode.HALT_P:
|
||||
case AmxOpCode.BOUNDS_P:
|
||||
case AmxOpCode.PUSH_P_ADR:
|
||||
break;
|
||||
|
||||
case AmxOpCode.PUSH3_C: /* instructions with 3 parameters */
|
||||
case AmxOpCode.PUSH3:
|
||||
case AmxOpCode.PUSH3_S:
|
||||
case AmxOpCode.PUSH3_ADR:
|
||||
cip += CellSize * 3;
|
||||
break;
|
||||
/* Packed Instructions referencing pointers */
|
||||
case AmxOpCode.LOAD_P_PRI:
|
||||
case AmxOpCode.LOAD_P_ALT:
|
||||
case AmxOpCode.INC_P:
|
||||
case AmxOpCode.DEC_P:
|
||||
GETPARAM_P( tgt, op );
|
||||
break;
|
||||
|
||||
case AmxOpCode.PUSH2_C: /* instructions with 2 parameters */
|
||||
case AmxOpCode.PUSH2:
|
||||
case AmxOpCode.PUSH2_S:
|
||||
case AmxOpCode.PUSH2_ADR:
|
||||
case AmxOpCode.CONST:
|
||||
case AmxOpCode.CONST_S:
|
||||
cip += CellSize * 2;
|
||||
break;
|
||||
/* Packed Instructions referencing stack */
|
||||
case AmxOpCode.LOAD_P_S_PRI:
|
||||
case AmxOpCode.LOAD_P_S_ALT:
|
||||
case AmxOpCode.LREF_P_S_PRI:
|
||||
case AmxOpCode.LREF_P_S_ALT:
|
||||
case AmxOpCode.INC_P_S:
|
||||
case AmxOpCode.DEC_P_S:
|
||||
GETPARAM_P( tgt, op ); /* verify address */
|
||||
break;
|
||||
|
||||
case AmxOpCode.LOAD_BOTH:
|
||||
// verify both
|
||||
cip += CellSize * 2;
|
||||
break;
|
||||
/* Single-Value Instructions */
|
||||
case AmxOpCode.LODB_I:
|
||||
case AmxOpCode.CONST_PRI:
|
||||
case AmxOpCode.CONST_ALT:
|
||||
case AmxOpCode.ADDR_PRI:
|
||||
case AmxOpCode.ADDR_ALT:
|
||||
case AmxOpCode.STRB_I:
|
||||
case AmxOpCode.LIDX_B:
|
||||
case AmxOpCode.IDXADDR_B:
|
||||
case AmxOpCode.ALIGN_PRI:
|
||||
case AmxOpCode.LCTRL:
|
||||
case AmxOpCode.SCTRL:
|
||||
case AmxOpCode.PICK:
|
||||
case AmxOpCode.PUSH_C:
|
||||
case AmxOpCode.PUSH:
|
||||
case AmxOpCode.PUSH_S:
|
||||
case AmxOpCode.STACK:
|
||||
case AmxOpCode.HEAP:
|
||||
case AmxOpCode.SHL_C_PRI:
|
||||
case AmxOpCode.SHL_C_ALT:
|
||||
case AmxOpCode.ADD_C:
|
||||
case AmxOpCode.SMUL_C:
|
||||
case AmxOpCode.ZERO:
|
||||
case AmxOpCode.ZERO_S:
|
||||
case AmxOpCode.EQ_C_PRI:
|
||||
case AmxOpCode.EQ_C_ALT:
|
||||
case AmxOpCode.MOVS:
|
||||
case AmxOpCode.CMPS:
|
||||
case AmxOpCode.FILL:
|
||||
case AmxOpCode.HALT:
|
||||
case AmxOpCode.BOUNDS:
|
||||
case AmxOpCode.PUSH_ADR:
|
||||
cip += CellSize;
|
||||
break;
|
||||
|
||||
case AmxOpCode.LOAD_S_BOTH:
|
||||
cip += CellSize * 2;
|
||||
break;
|
||||
case AmxOpCode.LOAD_PRI:
|
||||
case AmxOpCode.LOAD_ALT:
|
||||
case AmxOpCode.INC:
|
||||
case AmxOpCode.DEC:
|
||||
//VerifyAddress(0, );
|
||||
cip += CellSize;
|
||||
break;
|
||||
|
||||
case AmxOpCode.LODB_P_I: /* instructions with 1 parameter packed inside the same cell */
|
||||
case AmxOpCode.CONST_P_PRI:
|
||||
case AmxOpCode.CONST_P_ALT:
|
||||
case AmxOpCode.ADDR_P_PRI:
|
||||
case AmxOpCode.ADDR_P_ALT:
|
||||
case AmxOpCode.STRB_P_I:
|
||||
case AmxOpCode.LIDX_P_B:
|
||||
case AmxOpCode.IDXADDR_P_B:
|
||||
case AmxOpCode.ALIGN_P_PRI:
|
||||
case AmxOpCode.ALIGN_P_ALT:
|
||||
case AmxOpCode.PUSH_P_C:
|
||||
case AmxOpCode.PUSH_P:
|
||||
case AmxOpCode.PUSH_P_S:
|
||||
case AmxOpCode.STACK_P:
|
||||
case AmxOpCode.HEAP_P:
|
||||
case AmxOpCode.SHL_P_C_PRI:
|
||||
case AmxOpCode.SHL_P_C_ALT:
|
||||
case AmxOpCode.SHR_P_C_PRI:
|
||||
case AmxOpCode.SHR_P_C_ALT:
|
||||
case AmxOpCode.ADD_P_C:
|
||||
case AmxOpCode.SMUL_P_C:
|
||||
case AmxOpCode.ZERO_P:
|
||||
case AmxOpCode.ZERO_P_S:
|
||||
case AmxOpCode.EQ_P_C_PRI:
|
||||
case AmxOpCode.EQ_P_C_ALT:
|
||||
case AmxOpCode.MOVS_P:
|
||||
case AmxOpCode.CMPS_P:
|
||||
case AmxOpCode.FILL_P:
|
||||
case AmxOpCode.HALT_P:
|
||||
case AmxOpCode.BOUNDS_P:
|
||||
case AmxOpCode.PUSH_P_ADR:
|
||||
break;
|
||||
case AmxOpCode.LOAD_S_PRI:
|
||||
case AmxOpCode.LOAD_S_ALT:
|
||||
case AmxOpCode.LREF_S_PRI:
|
||||
case AmxOpCode.LREF_S_ALT:
|
||||
case AmxOpCode.INC_S:
|
||||
case AmxOpCode.DEC_S:
|
||||
cip += CellSize;
|
||||
break;
|
||||
|
||||
case AmxOpCode.LOAD_P_PRI: /* data instructions with 1 parameter packed inside the same cell */
|
||||
case AmxOpCode.LOAD_P_ALT:
|
||||
case AmxOpCode.LREF_P_PRI:
|
||||
case AmxOpCode.LREF_P_ALT:
|
||||
case AmxOpCode.STOR_P_PRI:
|
||||
case AmxOpCode.STOR_P_ALT:
|
||||
case AmxOpCode.SREF_P_PRI:
|
||||
case AmxOpCode.SREF_P_ALT:
|
||||
case AmxOpCode.INC_P:
|
||||
case AmxOpCode.DEC_P:
|
||||
GETPARAM_P(tgt, op);
|
||||
break;
|
||||
/* Parameterless Instructions */
|
||||
case AmxOpCode.LOAD_I:
|
||||
case AmxOpCode.STOR_I:
|
||||
case AmxOpCode.LIDX:
|
||||
case AmxOpCode.IDXADDR:
|
||||
case AmxOpCode.XCHG:
|
||||
case AmxOpCode.PUSH_PRI:
|
||||
case AmxOpCode.PUSH_ALT:
|
||||
case AmxOpCode.PPRI:
|
||||
case AmxOpCode.PALT:
|
||||
case AmxOpCode.PROC:
|
||||
case AmxOpCode.RET:
|
||||
case AmxOpCode.RETN:
|
||||
case AmxOpCode.SHL:
|
||||
case AmxOpCode.SHR:
|
||||
case AmxOpCode.SSHR:
|
||||
case AmxOpCode.SMUL:
|
||||
case AmxOpCode.SDIV:
|
||||
case AmxOpCode.ADD:
|
||||
case AmxOpCode.SUB:
|
||||
case AmxOpCode.AND:
|
||||
case AmxOpCode.OR:
|
||||
case AmxOpCode.XOR:
|
||||
case AmxOpCode.NOT:
|
||||
case AmxOpCode.NEG:
|
||||
case AmxOpCode.INVERT:
|
||||
case AmxOpCode.ZERO_PRI:
|
||||
case AmxOpCode.ZERO_ALT:
|
||||
case AmxOpCode.EQ:
|
||||
case AmxOpCode.NEQ:
|
||||
case AmxOpCode.SLESS:
|
||||
case AmxOpCode.SLEQ:
|
||||
case AmxOpCode.SGRTR:
|
||||
case AmxOpCode.SGEQ:
|
||||
case AmxOpCode.INC_PRI:
|
||||
case AmxOpCode.INC_ALT:
|
||||
case AmxOpCode.INC_I:
|
||||
case AmxOpCode.DEC_PRI:
|
||||
case AmxOpCode.DEC_ALT:
|
||||
case AmxOpCode.DEC_I:
|
||||
case AmxOpCode.SWAP_PRI:
|
||||
case AmxOpCode.SWAP_ALT:
|
||||
case AmxOpCode.NOP:
|
||||
case AmxOpCode.BREAK:
|
||||
break;
|
||||
|
||||
case AmxOpCode.LOAD_P_S_PRI: /* stack instructions with 1 parameter packed inside the same cell */
|
||||
case AmxOpCode.LOAD_P_S_ALT:
|
||||
case AmxOpCode.LREF_P_S_PRI:
|
||||
case AmxOpCode.LREF_P_S_ALT:
|
||||
case AmxOpCode.STOR_P_S_PRI:
|
||||
case AmxOpCode.STOR_P_S_ALT:
|
||||
case AmxOpCode.SREF_P_S_PRI:
|
||||
case AmxOpCode.SREF_P_S_ALT:
|
||||
case AmxOpCode.INC_P_S:
|
||||
case AmxOpCode.DEC_P_S:
|
||||
GETPARAM_P(tgt, op); /* verify address */
|
||||
break;
|
||||
/* Jump w/ Relocation */
|
||||
case AmxOpCode.CALL:
|
||||
case AmxOpCode.JUMP:
|
||||
case AmxOpCode.JZER:
|
||||
case AmxOpCode.JNZ:
|
||||
case AmxOpCode.JEQ:
|
||||
case AmxOpCode.JNEQ:
|
||||
case AmxOpCode.JSLESS:
|
||||
case AmxOpCode.JSLEQ:
|
||||
case AmxOpCode.JSGRTR:
|
||||
case AmxOpCode.JSGEQ:
|
||||
case AmxOpCode.SWITCH:
|
||||
/* if this file is an older version (absolute references instead of the
|
||||
* current use of position-independent code), convert the parameter
|
||||
* to position-independent code first
|
||||
*/
|
||||
cip += CellSize;
|
||||
break;
|
||||
|
||||
case AmxOpCode.LODB_I: /* instructions with 1 parameter (not packed) */
|
||||
case AmxOpCode.CONST_PRI:
|
||||
case AmxOpCode.CONST_ALT:
|
||||
case AmxOpCode.ADDR_PRI:
|
||||
case AmxOpCode.ADDR_ALT:
|
||||
case AmxOpCode.STRB_I:
|
||||
case AmxOpCode.LIDX_B:
|
||||
case AmxOpCode.IDXADDR_B:
|
||||
case AmxOpCode.ALIGN_PRI:
|
||||
case AmxOpCode.ALIGN_ALT:
|
||||
case AmxOpCode.LCTRL:
|
||||
case AmxOpCode.SCTRL:
|
||||
case AmxOpCode.PICK:
|
||||
case AmxOpCode.PUSH_C:
|
||||
case AmxOpCode.PUSH:
|
||||
case AmxOpCode.PUSH_S:
|
||||
case AmxOpCode.STACK:
|
||||
case AmxOpCode.HEAP:
|
||||
case AmxOpCode.JREL:
|
||||
case AmxOpCode.SHL_C_PRI:
|
||||
case AmxOpCode.SHL_C_ALT:
|
||||
case AmxOpCode.SHR_C_PRI:
|
||||
case AmxOpCode.SHR_C_ALT:
|
||||
case AmxOpCode.ADD_C:
|
||||
case AmxOpCode.SMUL_C:
|
||||
case AmxOpCode.ZERO:
|
||||
case AmxOpCode.ZERO_S:
|
||||
case AmxOpCode.EQ_C_PRI:
|
||||
case AmxOpCode.EQ_C_ALT:
|
||||
case AmxOpCode.MOVS:
|
||||
case AmxOpCode.CMPS:
|
||||
case AmxOpCode.FILL:
|
||||
case AmxOpCode.HALT:
|
||||
case AmxOpCode.BOUNDS:
|
||||
case AmxOpCode.PUSH_ADR:
|
||||
cip += CellSize;
|
||||
break;
|
||||
/* overlay opcodes (overlays must be enabled) */
|
||||
case AmxOpCode.ISWITCH:
|
||||
Debug.Assert( Header.FileVersion >= 10 );
|
||||
/* drop through */
|
||||
goto case AmxOpCode.ICALL;
|
||||
case AmxOpCode.ICALL:
|
||||
cip += CellSize;
|
||||
/* drop through */
|
||||
goto case AmxOpCode.IRETN;
|
||||
case AmxOpCode.IRETN:
|
||||
Debug.Assert( Header.Overlays != 0 && Header.Overlays != Header.NameTable );
|
||||
//return AmxError.OVERLAY; /* no overlay callback */
|
||||
break;
|
||||
case AmxOpCode.ICASETBL:
|
||||
{
|
||||
Cell num;
|
||||
//DBGPARAM(num); /* number of records follows the opcode */
|
||||
//cip += (2 * num + 1) * CellSize;
|
||||
//if (Header.Overlays == 0)
|
||||
// return AmxError.OVERLAY; /* no overlay callback */
|
||||
break;
|
||||
} /* case */
|
||||
|
||||
case AmxOpCode.LOAD_PRI:
|
||||
case AmxOpCode.LOAD_ALT:
|
||||
case AmxOpCode.LREF_PRI:
|
||||
case AmxOpCode.LREF_ALT:
|
||||
case AmxOpCode.STOR_PRI:
|
||||
case AmxOpCode.STOR_ALT:
|
||||
case AmxOpCode.SREF_PRI:
|
||||
case AmxOpCode.SREF_ALT:
|
||||
case AmxOpCode.INC:
|
||||
case AmxOpCode.DEC:
|
||||
//VerifyAddress(0, );
|
||||
cip += CellSize;
|
||||
break;
|
||||
case AmxOpCode.SYSREQ_C:
|
||||
cip += CellSize;
|
||||
sysreq_flg |= 0x01; /* mark SYSREQ found */
|
||||
break;
|
||||
case AmxOpCode.SYSREQ_N:
|
||||
cip += CellSize * 2;
|
||||
sysreq_flg |= 0x02; /* mark SYSREQ.N found */
|
||||
break;
|
||||
|
||||
case AmxOpCode.LOAD_S_PRI:
|
||||
case AmxOpCode.LOAD_S_ALT:
|
||||
case AmxOpCode.LREF_S_PRI:
|
||||
case AmxOpCode.LREF_S_ALT:
|
||||
case AmxOpCode.STOR_S_PRI:
|
||||
case AmxOpCode.STOR_S_ALT:
|
||||
case AmxOpCode.SREF_S_PRI:
|
||||
case AmxOpCode.SREF_S_ALT:
|
||||
case AmxOpCode.INC_S:
|
||||
case AmxOpCode.DEC_S:
|
||||
cip += CellSize;
|
||||
break;
|
||||
case AmxOpCode.CASETBL:
|
||||
{
|
||||
DBGPARAM( out var num );
|
||||
//cip += (2 * num + 1) * CellSize;
|
||||
break;
|
||||
}
|
||||
|
||||
case AmxOpCode.LOAD_I: /* instructions without parameters */
|
||||
case AmxOpCode.STOR_I:
|
||||
case AmxOpCode.LIDX:
|
||||
case AmxOpCode.IDXADDR:
|
||||
case AmxOpCode.MOVE_PRI:
|
||||
case AmxOpCode.MOVE_ALT:
|
||||
case AmxOpCode.XCHG:
|
||||
case AmxOpCode.PUSH_PRI:
|
||||
case AmxOpCode.PUSH_ALT:
|
||||
case AmxOpCode.POP_PRI:
|
||||
case AmxOpCode.POP_ALT:
|
||||
case AmxOpCode.PROC:
|
||||
case AmxOpCode.RET:
|
||||
case AmxOpCode.RETN:
|
||||
//case AmxOpCode.CALL_PRI:
|
||||
case AmxOpCode.SHL:
|
||||
case AmxOpCode.SHR:
|
||||
case AmxOpCode.SSHR:
|
||||
case AmxOpCode.SMUL:
|
||||
case AmxOpCode.SDIV:
|
||||
case AmxOpCode.SDIV_ALT:
|
||||
case AmxOpCode.UMUL:
|
||||
case AmxOpCode.UDIV:
|
||||
case AmxOpCode.UDIV_ALT:
|
||||
case AmxOpCode.ADD:
|
||||
case AmxOpCode.SUB:
|
||||
case AmxOpCode.SUB_ALT:
|
||||
case AmxOpCode.AND:
|
||||
case AmxOpCode.OR:
|
||||
case AmxOpCode.XOR:
|
||||
case AmxOpCode.NOT:
|
||||
case AmxOpCode.NEG:
|
||||
case AmxOpCode.INVERT:
|
||||
case AmxOpCode.ZERO_PRI:
|
||||
case AmxOpCode.ZERO_ALT:
|
||||
case AmxOpCode.SIGN_PRI:
|
||||
case AmxOpCode.SIGN_ALT:
|
||||
case AmxOpCode.EQ:
|
||||
case AmxOpCode.NEQ:
|
||||
case AmxOpCode.LESS:
|
||||
case AmxOpCode.LEQ:
|
||||
case AmxOpCode.GRTR:
|
||||
case AmxOpCode.GEQ:
|
||||
case AmxOpCode.SLESS:
|
||||
case AmxOpCode.SLEQ:
|
||||
case AmxOpCode.SGRTR:
|
||||
case AmxOpCode.SGEQ:
|
||||
case AmxOpCode.INC_PRI:
|
||||
case AmxOpCode.INC_ALT:
|
||||
case AmxOpCode.INC_I:
|
||||
case AmxOpCode.DEC_PRI:
|
||||
case AmxOpCode.DEC_ALT:
|
||||
case AmxOpCode.DEC_I:
|
||||
//case AmxOpCode.SYSREQ_PRI:
|
||||
//case AmxOpCode.JUMP_PRI:
|
||||
case AmxOpCode.SWAP_PRI:
|
||||
case AmxOpCode.SWAP_ALT:
|
||||
case AmxOpCode.NOP:
|
||||
case AmxOpCode.BREAK:
|
||||
break;
|
||||
default:
|
||||
Header.Flags &= ~AmxFlags.VERIFY;
|
||||
//return AmxError.INVINSTR;
|
||||
break;
|
||||
}
|
||||
|
||||
case AmxOpCode.CALL: /* opcodes that need relocation (JIT only), or conversion to position-independent code */
|
||||
case AmxOpCode.JUMP:
|
||||
case AmxOpCode.JZER:
|
||||
case AmxOpCode.JNZ:
|
||||
case AmxOpCode.JEQ:
|
||||
case AmxOpCode.JNEQ:
|
||||
case AmxOpCode.JLESS:
|
||||
case AmxOpCode.JLEQ:
|
||||
case AmxOpCode.JGRTR:
|
||||
case AmxOpCode.JGEQ:
|
||||
case AmxOpCode.JSLESS:
|
||||
case AmxOpCode.JSLEQ:
|
||||
case AmxOpCode.JSGRTR:
|
||||
case AmxOpCode.JSGEQ:
|
||||
case AmxOpCode.SWITCH:
|
||||
/* if this file is an older version (absolute references instead of the
|
||||
* current use of position-independent code), convert the parameter
|
||||
* to position-independent code first
|
||||
*/
|
||||
cip += CellSize;
|
||||
break;
|
||||
|
||||
/* overlay opcodes (overlays must be enabled) */
|
||||
case AmxOpCode.ISWITCH:
|
||||
Debug.Assert(Header.FileVersion >= 10);
|
||||
/* drop through */
|
||||
goto case AmxOpCode.ICALL;
|
||||
case AmxOpCode.ICALL:
|
||||
cip += CellSize;
|
||||
/* drop through */
|
||||
goto case AmxOpCode.IRETN;
|
||||
case AmxOpCode.IRETN:
|
||||
Debug.Assert(Header.Overlays != 0 && Header.Overlays != Header.NameTable);
|
||||
//return AmxError.OVERLAY; /* no overlay callback */
|
||||
break;
|
||||
case AmxOpCode.ICASETBL:
|
||||
{
|
||||
Cell num;
|
||||
//DBGPARAM(num); /* number of records follows the opcode */
|
||||
//cip += (2 * num + 1) * CellSize;
|
||||
//if (Header.Overlays == 0)
|
||||
// return AmxError.OVERLAY; /* no overlay callback */
|
||||
break;
|
||||
} /* case */
|
||||
|
||||
case AmxOpCode.SYSREQ_C:
|
||||
cip += CellSize;
|
||||
sysreq_flg |= 0x01; /* mark SYSREQ.C found */
|
||||
break;
|
||||
case AmxOpCode.SYSREQ_N:
|
||||
cip += CellSize * 2;
|
||||
sysreq_flg |= 0x02; /* mark SYSREQ.N found */
|
||||
break;
|
||||
|
||||
case AmxOpCode.CASETBL:
|
||||
{
|
||||
DBGPARAM(out var num);
|
||||
//cip += (2 * num + 1) * CellSize;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
Header.Flags &= ~AmxFlags.VERIFY;
|
||||
//return AmxError.INVINSTR;
|
||||
break;
|
||||
}
|
||||
|
||||
void DBGPARAM(out Cell v) => v = null; // v = (Cell)(amx->code + (int)cip), cip += CellSize)
|
||||
void DBGPARAM( out Cell v ) => v = null; // v = (Cell)(amx->code + (int)cip), cip += CellSize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -121,9 +121,9 @@ public class AmxDebugHeader
|
|||
public const int SIZE = 36;
|
||||
}
|
||||
|
||||
public class Public
|
||||
public class TableRecord
|
||||
{
|
||||
public Public(string name, uint address)
|
||||
public TableRecord(string name, uint address)
|
||||
{
|
||||
Name = name;
|
||||
Address = address;
|
||||
|
|
|
|||
|
|
@ -3,228 +3,228 @@
|
|||
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,
|
||||
POP_PRI,
|
||||
POP_ALT,
|
||||
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
|
||||
}
|
||||
public enum AmxOpCode : uint
|
||||
{
|
||||
NONE,
|
||||
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,
|
||||
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,
|
||||
LINE,
|
||||
SYMBOL,
|
||||
SRANGE,
|
||||
JUMP_PRI,
|
||||
SWITCH,
|
||||
CASETBL,
|
||||
SWAP_PRI,
|
||||
SWAP_ALT,
|
||||
PUSH_ADR,
|
||||
NOP,
|
||||
SYSREQ_N,
|
||||
SYMTAG,
|
||||
BREAK,
|
||||
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 */
|
||||
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,
|
||||
|
||||
SYSREQ_D,
|
||||
SYSREQ_ND,
|
||||
NUM_OPCODES,
|
||||
}
|
||||
|
||||
public static class AmxOpCodeExtensions
|
||||
{
|
||||
|
|
|
|||
22
pkNX.Structures/Scripts/AmxOpCodeType.cs
Normal file
22
pkNX.Structures/Scripts/AmxOpCodeType.cs
Normal file
|
|
@ -0,0 +1,22 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public enum AmxOpCodeType
|
||||
{
|
||||
Unknown = -1,
|
||||
NoParams,
|
||||
OneParam,
|
||||
TwoParams,
|
||||
ThreeParams,
|
||||
FourParams,
|
||||
FiveParams,
|
||||
Jump,
|
||||
Packed,
|
||||
CaseTable,
|
||||
}
|
||||
}
|
||||
228
pkNX.Structures/Scripts/OpCodeTypeMappings.cs
Normal file
228
pkNX.Structures/Scripts/OpCodeTypeMappings.cs
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static partial class PawnUtil
|
||||
{
|
||||
public static readonly Dictionary<AmxOpCode, AmxOpCodeType> OpCodeTypes = new Dictionary<AmxOpCode, AmxOpCodeType> {
|
||||
{ AmxOpCode.NONE, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.LOAD_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LOAD_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LOAD_S_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LOAD_S_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LREF_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LREF_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LREF_S_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LREF_S_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LOAD_I, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.LODB_I, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.CONST_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.CONST_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ADDR_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ADDR_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.STOR_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.STOR_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.STOR_S_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.STOR_S_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SREF_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SREF_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SREF_S_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SREF_S_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.STOR_I, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.STRB_I, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LIDX, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.LIDX_B, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.IDXADDR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.IDXADDR_B, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ALIGN_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ALIGN_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.LCTRL, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SCTRL, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.MOVE_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.MOVE_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.XCHG, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PUSH_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PUSH_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PICK, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.PUSH_C, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.PUSH, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.PUSH_S, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.PPRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.STACK, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.HEAP, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.PROC, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.RET, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.RETN, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.CALL, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.CALL_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.JUMP, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JREL, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JZER, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JNZ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JNEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JLESS, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JLEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JGRTR, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JGEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JSLESS, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JSLEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JSGRTR, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.JSGEQ, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.SHL, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SHR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SSHR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SHL_C_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SHL_C_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SHR_C_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SHR_C_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SMUL, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SDIV, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SDIV_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.UMUL, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.UDIV, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.UDIV_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.ADD, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SUB, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SUB_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.AND, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.OR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.XOR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.NOT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.NEG, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.INVERT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.ADD_C, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SMUL_C, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ZERO_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.ZERO_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.ZERO, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.ZERO_S, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SIGN_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SIGN_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.EQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.NEQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.LESS, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.LEQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.GRTR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.GEQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SLESS, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SLEQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SGRTR, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SGEQ, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.EQ_C_PRI, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.EQ_C_ALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.INC_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.INC_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.INC, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.INC_S, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.INC_I, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.DEC_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.DEC_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.DEC, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.DEC_S, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.DEC_I, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.MOVS, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.CMPS, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.FILL, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.HALT, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.BOUNDS, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SYSREQ_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SYSREQ_C, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.FILE, AmxOpCodeType.ThreeParams },
|
||||
{ AmxOpCode.LINE, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.SYMBOL, AmxOpCodeType.FourParams },
|
||||
{ AmxOpCode.SRANGE, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.JUMP_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SWITCH, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.CASETBL, AmxOpCodeType.CaseTable },
|
||||
{ AmxOpCode.SWAP_PRI, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SWAP_ALT, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PUSH_ADR, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.NOP, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.SYSREQ_N, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.SYMTAG, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.BREAK, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.PUSH2_C, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.PUSH2, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.PUSH2_S, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.PUSH2_ADR, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.PUSH3_C, AmxOpCodeType.ThreeParams },
|
||||
{ AmxOpCode.PUSH3, AmxOpCodeType.ThreeParams },
|
||||
{ AmxOpCode.PUSH3_S, AmxOpCodeType.ThreeParams },
|
||||
{ AmxOpCode.PUSH3_ADR, AmxOpCodeType.ThreeParams },
|
||||
{ AmxOpCode.PUSH4_C, AmxOpCodeType.FourParams },
|
||||
{ AmxOpCode.PUSH4, AmxOpCodeType.FourParams },
|
||||
{ AmxOpCode.PUSH4_S, AmxOpCodeType.FourParams },
|
||||
{ AmxOpCode.PUSH4_ADR, AmxOpCodeType.FourParams },
|
||||
{ AmxOpCode.PUSH5_C, AmxOpCodeType.FiveParams },
|
||||
{ AmxOpCode.PUSH5, AmxOpCodeType.FiveParams },
|
||||
{ AmxOpCode.PUSH5_S, AmxOpCodeType.FiveParams },
|
||||
{ AmxOpCode.PUSH5_ADR, AmxOpCodeType.FiveParams },
|
||||
{ AmxOpCode.LOAD_BOTH, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.LOAD_S_BOTH, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.CONST, AmxOpCodeType.TwoParams },
|
||||
{ AmxOpCode.CONST_S, AmxOpCodeType.TwoParams },
|
||||
/* overlay instructions */
|
||||
{ AmxOpCode.ICALL, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.IRETN, AmxOpCodeType.NoParams },
|
||||
{ AmxOpCode.ISWITCH, AmxOpCodeType.Jump },
|
||||
{ AmxOpCode.ICASETBL, AmxOpCodeType.CaseTable },
|
||||
/* packed instructions */
|
||||
{ AmxOpCode.LOAD_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LOAD_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LOAD_P_S_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LOAD_P_S_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LREF_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LREF_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LREF_P_S_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LREF_P_S_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LODB_P_I, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.CONST_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.CONST_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ADDR_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ADDR_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STOR_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STOR_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STOR_P_S_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STOR_P_S_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SREF_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SREF_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SREF_P_S_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SREF_P_S_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STRB_P_I, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.LIDX_P_B, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.IDXADDR_P_B, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ALIGN_P_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ALIGN_P_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.PUSH_P_C, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.PUSH_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.PUSH_P_S, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.STACK_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.HEAP_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SHL_P_C_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SHL_P_C_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SHR_P_C_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SHR_P_C_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ADD_P_C, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.SMUL_P_C, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ZERO_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.ZERO_P_S, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.EQ_P_C_PRI, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.EQ_P_C_ALT, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.INC_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.INC_P_S, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.DEC_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.DEC_P_S, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.MOVS_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.CMPS_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.FILL_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.HALT_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.BOUNDS_P, AmxOpCodeType.Packed },
|
||||
{ AmxOpCode.PUSH_P_ADR, AmxOpCodeType.Packed },
|
||||
|
||||
{ AmxOpCode.SYSREQ_D, AmxOpCodeType.OneParam },
|
||||
{ AmxOpCode.SYSREQ_ND, AmxOpCodeType.TwoParams },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
@ -5,548 +5,340 @@
|
|||
|
||||
namespace pkNX.Structures
|
||||
{
|
||||
public static class PawnUtil
|
||||
{
|
||||
// FireFly's (github.com/FireyFly) concise decompression (ported c->c#):
|
||||
// https://github.com/FireyFly/poketools/blob/e74538a5b5e5dab1e78c1cd313c55d158f37534d/src/formats/script.c#L61
|
||||
public static uint[] QuickDecompress(byte[] data, int count)
|
||||
{
|
||||
uint[] code = new uint[count];
|
||||
uint i = 0, j = 0, x = 0, f = 0;
|
||||
while (i < code.Length)
|
||||
{
|
||||
int b = data[f++],
|
||||
v = b & 0x7F;
|
||||
if (++j == 1) // sign extension possible
|
||||
x = (uint)((((v >> 6 == 0 ? 1 : 0) - 1) << 6) | v); // only for bit6 being set
|
||||
else
|
||||
x = (x << 7) | (byte)v; // shift data into place
|
||||
public static partial class PawnUtil
|
||||
{
|
||||
// FireFly's (github.com/FireyFly) concise decompression (ported c->c#):
|
||||
// https://github.com/FireyFly/poketools/blob/e74538a5b5e5dab1e78c1cd313c55d158f37534d/src/formats/script.c#L61
|
||||
public static uint[] QuickDecompress( byte[] data, int count )
|
||||
{
|
||||
uint[] code = new uint[ count ];
|
||||
uint i = 0, j = 0, x = 0, f = 0;
|
||||
while ( i < code.Length )
|
||||
{
|
||||
int b = data[ f++ ],
|
||||
v = b & 0x7F,
|
||||
c = b & 0x80;
|
||||
if ( ++j == 1 ) // sign extension possible
|
||||
x = (uint) ( ( ( ( v >> 6 == 0 ? 1 : 0 ) - 1 ) << 6 ) | v ); // only for bit6 being set
|
||||
else
|
||||
x = ( x << 7 ) | (byte) v; // shift data into place
|
||||
|
||||
if ((b & 0x80) != 0)
|
||||
continue; // more data to read
|
||||
code[i++] = x; j = 0; // write finalized instruction
|
||||
}
|
||||
return code;
|
||||
}
|
||||
if ( c > 0 )
|
||||
continue; // more data to read
|
||||
|
||||
// Compression
|
||||
public static byte[] CompressScript(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length % 4 != 0) // Bad Input
|
||||
return null;
|
||||
using (MemoryStream mn = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(mn))
|
||||
{
|
||||
int pos = 0;
|
||||
while (pos < data.Length)
|
||||
{
|
||||
byte[] db = data.Skip(pos += 4).Take(4).ToArray();
|
||||
byte[] cb = CompressBytes(db);
|
||||
bw.Write(cb);
|
||||
}
|
||||
return mn.ToArray();
|
||||
}
|
||||
}
|
||||
code[ i++ ] = x;
|
||||
j = 0; // write finalized instruction
|
||||
}
|
||||
|
||||
public static byte[] CompressBytes(byte[] db)
|
||||
{
|
||||
short cmd = BitConverter.ToInt16(db, 0);
|
||||
short val = BitConverter.ToInt16(db, 2);
|
||||
return code;
|
||||
}
|
||||
|
||||
byte[] cb = Array.Empty<byte>();
|
||||
bool sign4 = val < 0 && cmd < 0 && db[0] >= 0xC0; // 4 byte signed
|
||||
bool sign3 = val < 0 && cmd < 0 && db[0] < 0xC0; // 3 byte signed
|
||||
bool sign2 = val < 0 && cmd > 0; // 2 byte signed
|
||||
bool liter = cmd >= 0 && cmd < 0x40; // Literal
|
||||
bool manyb = cmd >= 0x40; // manybit
|
||||
// https://github.com/gameswop/mtasa-resources/blob/d557a72fefef57ac34780a76edf16383d3dff0e8/%5Bgamemodes%5D/%5Bamx%5D/amx-deps/src/amx/amx.c#L1119
|
||||
// Slightly more readable, but potentially slower
|
||||
public static uint[] QuickDecompress2( byte[] data, int count )
|
||||
{
|
||||
var memsize = count * sizeof( uint );
|
||||
var instructions = new uint[ count ];
|
||||
uint cell;
|
||||
int shift;
|
||||
int i = data.Length;
|
||||
|
||||
if (sign4)
|
||||
{
|
||||
int dev = 0x40 + BitConverter.ToInt32(db, 0);
|
||||
if (dev < 0) // BADLOGIC
|
||||
return cb;
|
||||
cb = new[] { (byte)((dev & 0x3F) | 0x40) };
|
||||
}
|
||||
else if (sign3)
|
||||
{
|
||||
byte dev = (byte)(((db[1] << 1) + 0x40) | 0xC0 | db[0] >> 7);
|
||||
byte low = db[0];
|
||||
cb = new[] { dev, low };
|
||||
}
|
||||
else if (sign2)
|
||||
{
|
||||
if (manyb)
|
||||
{
|
||||
byte dev = (byte)(((db[2] << 2) + 0x40) | 0xC0 | db[1] >> 7);
|
||||
byte low1 = (byte)(0x80 | (db[0] >> 7) | (db[1] & 0x80));
|
||||
byte low0 = (byte)(db[0] & 0x80);
|
||||
cb = new[] { low0, low1, dev };
|
||||
}
|
||||
else // Dunno if this ever naturally happens; the command reader may ignore db[1] if db[0] < 0x80... needs verification.
|
||||
{
|
||||
byte dev = (byte)(((db[1] << 2) + 0x40) | 0xC0 | db[0] >> 6);
|
||||
byte low0 = (byte)(db[0] & 0x3F);
|
||||
cb = new[] { low0, dev };
|
||||
}
|
||||
}
|
||||
else if (manyb)
|
||||
{
|
||||
ulong bitStorage = 0;
|
||||
while ( i > 0 )
|
||||
{
|
||||
cell = 0;
|
||||
shift = 0;
|
||||
|
||||
uint dv = BitConverter.ToUInt32(db, 0);
|
||||
int ctr = 0;
|
||||
while (dv != 0) // bits remaining
|
||||
{
|
||||
byte bits = (byte)((byte)dv & 0x7F); dv >>= 7; // Take off 7 bits at a time
|
||||
bitStorage |= (byte)(bits << (ctr * 8)); // Write the 7 bits into storage
|
||||
bitStorage |= (byte)(1 << (7 + (ctr++ * 8))); // continue reading flag
|
||||
}
|
||||
byte[] compressedBits = BitConverter.GetBytes(bitStorage);
|
||||
do
|
||||
{
|
||||
i--;
|
||||
cell |= (uint) ( data[ i ] & 0x7f ) << shift;
|
||||
shift += 7;
|
||||
}
|
||||
while ( i > 0 && ( data[ i - 1 ] & 0x80 ) != 0 );
|
||||
|
||||
Array.Reverse(compressedBits);
|
||||
// Trim off leading zero-bytes
|
||||
cb = compressedBits.SkipWhile(v => v == 0).ToArray();
|
||||
}
|
||||
else if (liter)
|
||||
{
|
||||
cb = new[] { (byte)cmd };
|
||||
}
|
||||
return cb;
|
||||
}
|
||||
if ( ( data[ i ] & 0x40 ) != 0 )
|
||||
{
|
||||
while ( shift < 8 * sizeof( uint ) )
|
||||
{
|
||||
cell |= (uint) 0xff << shift;
|
||||
shift += 8;
|
||||
}
|
||||
}
|
||||
|
||||
// Interpreting
|
||||
public static string[] ParseScript(uint[] cmd, int sanity = -1)
|
||||
{
|
||||
// sub_148CBC Moon v1.0
|
||||
List<string> parse = new List<string>();
|
||||
int sanityMode = 0;
|
||||
memsize -= sizeof( uint );
|
||||
instructions[ memsize / sizeof( uint ) ] = cell;
|
||||
}
|
||||
|
||||
int i = 0; // Current Offset of decompressed instructions
|
||||
while (i < cmd.Length) // read away
|
||||
{
|
||||
// Read a Command
|
||||
int line = i;
|
||||
uint c = cmd[i++];
|
||||
return instructions;
|
||||
}
|
||||
|
||||
string op;
|
||||
switch (c & 0x7FFF)
|
||||
{
|
||||
default:
|
||||
throw new ArgumentException("Invalid Command ID");
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
case 0x05:
|
||||
case 0x06:
|
||||
case 0x0F:
|
||||
case 0x10:
|
||||
case 0x13:
|
||||
case 0x14:
|
||||
case 0x6D:
|
||||
case 0x72:
|
||||
{
|
||||
// Peek at next value
|
||||
var next = (int)cmd[i++];
|
||||
// Check Value against negative and zero... ?
|
||||
// Compression
|
||||
/*public static byte[] CompressScript(byte[] data)
|
||||
{
|
||||
if (data == null || data.Length % 4 != 0) // Bad Input
|
||||
return null;
|
||||
using (MemoryStream mn = new MemoryStream())
|
||||
using (BinaryWriter bw = new BinaryWriter(mn))
|
||||
{
|
||||
for ( var pos = 0; pos < data.Length; pos += 4 )
|
||||
{
|
||||
byte[] db = data.Skip(pos).Take(4).ToArray();
|
||||
byte[] cb = CompressBytes(db);
|
||||
bw.Write(cb);
|
||||
}
|
||||
return mn.ToArray();
|
||||
}
|
||||
}*/
|
||||
|
||||
op = EA(c, next);
|
||||
break;
|
||||
}
|
||||
case 0x03:
|
||||
case 0x04:
|
||||
case 0x07:
|
||||
case 0x08:
|
||||
case 0x11:
|
||||
case 0x12:
|
||||
case 0x15:
|
||||
case 0x16:
|
||||
case 0x6E:
|
||||
case 0x73:
|
||||
{
|
||||
// Peek at next value
|
||||
var next = (int)cmd[i++];
|
||||
// Check Value against negative... ?
|
||||
public static byte[] CompressScript( uint[] instructions )
|
||||
=> instructions.SelectMany( CompressInstruction ).ToArray();
|
||||
|
||||
op = EA(c, next);
|
||||
break;
|
||||
}
|
||||
case 0x09:
|
||||
case 0x17:
|
||||
case 0x19:
|
||||
case 0x1B:
|
||||
case 0x21:
|
||||
case 0x22:
|
||||
case 0x23:
|
||||
case 0x24:
|
||||
case 0x25:
|
||||
case 0x2A:
|
||||
case 0x2B:
|
||||
case 0x2E: // Begin
|
||||
case 0x2F:
|
||||
case 0x30: // Return
|
||||
case 0x41:
|
||||
case 0x42:
|
||||
case 0x43:
|
||||
case 0x48:
|
||||
case 0x49:
|
||||
case 0x4A:
|
||||
case 0x4B:
|
||||
case 0x4C:
|
||||
case 0x4D:
|
||||
case 0x4E: // Add?
|
||||
case 0x4F:
|
||||
case 0x50:
|
||||
case 0x51: // Cmp?
|
||||
case 0x52:
|
||||
case 0x53:
|
||||
case 0x54:
|
||||
case 0x55:
|
||||
case 0x56:
|
||||
case 0x59: // ClearAll
|
||||
case 0x5A:
|
||||
case 0x5D:
|
||||
case 0x5E:
|
||||
case 0x5F:
|
||||
case 0x60:
|
||||
case 0x61:
|
||||
case 0x62:
|
||||
case 0x63:
|
||||
case 0x64:
|
||||
case 0x65:
|
||||
case 0x66:
|
||||
case 0x67:
|
||||
case 0x68:
|
||||
case 0x6B:
|
||||
case 0x6C:
|
||||
case 0x6F:
|
||||
case 0x70:
|
||||
case 0x71:
|
||||
case 0x74:
|
||||
case 0x7A:
|
||||
case 0x83:
|
||||
case 0x84:
|
||||
case 0x86:
|
||||
case 0x89: // LineNo?
|
||||
case 0xAA:
|
||||
case 0xAB: // PushConst2
|
||||
case 0xAC: // CmpConst2
|
||||
case 0xAD:
|
||||
case 0xAE:
|
||||
case 0xB7:
|
||||
case 0xB8:
|
||||
case 0xB9:
|
||||
case 0xBA:
|
||||
case 0xBB:
|
||||
case 0xBC: // PushConst
|
||||
case 0xBD:
|
||||
case 0xBE:
|
||||
case 0xBF: // AdjustStack
|
||||
case 0xC0:
|
||||
case 0xC1:
|
||||
case 0xC2:
|
||||
case 0xC3:
|
||||
case 0xC4:
|
||||
case 0xC5:
|
||||
case 0xC6:
|
||||
case 0xC7:
|
||||
case 0xC8: // CmpLocal
|
||||
case 0xC9: // CmpConst
|
||||
case 0xCA:
|
||||
case 0xCF:
|
||||
case 0xD0:
|
||||
case 0xD1:
|
||||
case 0xD2:
|
||||
case 0xD3:
|
||||
case 0xD4:
|
||||
{
|
||||
// no sanity checks
|
||||
var arg = (short)(c >> 16);
|
||||
private static byte[] CompressInstruction( uint instruction )
|
||||
{
|
||||
var bytes = new List<byte>();
|
||||
var sign = ( instruction & 0x80000000 ) > 0;
|
||||
|
||||
op = EA(c & 0xFF, arg);
|
||||
// Signed (negative) values are handled opposite of unsigned (positive) values.
|
||||
// Positive values are "done" when we've shifted the value down to zero, but
|
||||
// we don't need to store the highest 1s in a signed value. We handle this by
|
||||
// tracking the loop via a NOTed shadow copy of the instruction if it's signed.
|
||||
var shadow = sign ? ~instruction : instruction;
|
||||
|
||||
if ((c & 0xFF) == 0x30) // return
|
||||
op += Environment.NewLine;
|
||||
break;
|
||||
}
|
||||
case 0x0A:
|
||||
case 0x0B:
|
||||
case 0x0C:
|
||||
case 0x0D:
|
||||
case 0x0E:
|
||||
case 0x18:
|
||||
case 0x1A:
|
||||
case 0x1C:
|
||||
case 0x1D:
|
||||
case 0x1E:
|
||||
case 0x1F:
|
||||
case 0x20:
|
||||
case 0x26:
|
||||
case 0x27: // PushConst
|
||||
case 0x28:
|
||||
case 0x29:
|
||||
case 0x2C:
|
||||
case 0x2D:
|
||||
case 0x34:
|
||||
case 0x44:
|
||||
case 0x45:
|
||||
case 0x46:
|
||||
case 0x47:
|
||||
case 0x57:
|
||||
case 0x58:
|
||||
case 0x5B:
|
||||
case 0x5C:
|
||||
case 0x69:
|
||||
case 0x6A:
|
||||
case 0x75:
|
||||
case 0x76:
|
||||
case 0x77:
|
||||
case 0x78:
|
||||
case 0x79:
|
||||
case 0x85:
|
||||
{
|
||||
var next = (int)cmd[i++];
|
||||
// No sanity check needed
|
||||
do
|
||||
{
|
||||
var least7 = instruction & 0b01111111;
|
||||
var byteVal = (byte) least7;
|
||||
|
||||
op = EA(c, next);
|
||||
break;
|
||||
}
|
||||
if ( bytes.Any() )
|
||||
// Continuation bit on all but the lowest byte
|
||||
byteVal |= 0x80;
|
||||
|
||||
case 0x31: // CallFunc
|
||||
case 0x33:
|
||||
case 0x35: // Jump!=
|
||||
case 0x36: // Jump==
|
||||
case 0x37:
|
||||
case 0x38:
|
||||
case 0x39:
|
||||
case 0x3A:
|
||||
case 0x3B:
|
||||
case 0x3C:
|
||||
case 0x3D:
|
||||
case 0x3E:
|
||||
case 0x3F:
|
||||
case 0x40:
|
||||
case 0x81: // Jump
|
||||
{
|
||||
var delta = (int)cmd[i++];
|
||||
// sanity check range...
|
||||
// negative.. weird
|
||||
bytes.Add( byteVal );
|
||||
|
||||
int newOfs = (line * 4) + delta;
|
||||
op = $"{Commands[c]} => 0x{newOfs:X4} ({delta})";
|
||||
break;
|
||||
}
|
||||
case 0x7B:
|
||||
{
|
||||
var next = (int)cmd[i++];
|
||||
sanityMode |= 1; // flag mode 1
|
||||
instruction >>= 7;
|
||||
shadow >>= 7;
|
||||
}
|
||||
while ( shadow != 0 );
|
||||
|
||||
op = EA(c, next);
|
||||
break;
|
||||
}
|
||||
case 0x82: // JumpIfElse
|
||||
{
|
||||
//var jOffset = (i * 4) - 4; // this may be the correct jump start point...
|
||||
var count = cmd[i++]; // switch case table
|
||||
// sanity check
|
||||
if ( bytes.Count < 5 )
|
||||
{
|
||||
// Ensure "sign bit" (bit just to the right of highest continuation bit) is
|
||||
// correct. Add an extra empty continuation byte if we need to. Values can't
|
||||
// be longer than 5 bytes, though.
|
||||
|
||||
// Populate If-Case Tree
|
||||
var tree = new List<string>();
|
||||
var signBit = sign ? 0x40 : 0x00;
|
||||
|
||||
// Cases
|
||||
for (int j = 0; j < count; j++)
|
||||
{
|
||||
var jmp = (int)cmd[i++];
|
||||
var toOffset = ((i - 2) * 4) + jmp;
|
||||
var ifValue = (int)cmd[i++];
|
||||
tree.Add($"\t{ifValue} => 0x{toOffset:X4} ({jmp})");
|
||||
}
|
||||
// Default
|
||||
{
|
||||
int jmp = (int)cmd[i++];
|
||||
var toOffset = ((i - 2) * 4) + jmp;
|
||||
tree.Add($"\t* => 0x{toOffset:X4} ({jmp})");
|
||||
}
|
||||
if ( ( bytes.Last() & 0x40 ) != signBit )
|
||||
bytes.Add( (byte) ( sign ? 0xFF : 0x80 ) );
|
||||
}
|
||||
|
||||
op = Commands[c] + Environment.NewLine + string.Join(Environment.NewLine, tree);
|
||||
break;
|
||||
}
|
||||
case 0x87:
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
var next2 = (int)cmd[i++];
|
||||
sanityMode |= 2; // flag mode 2
|
||||
// Little endian to big endian
|
||||
bytes.Reverse();
|
||||
|
||||
op = EA(c, next1, next2);
|
||||
break;
|
||||
}
|
||||
return bytes.ToArray();
|
||||
}
|
||||
|
||||
case 0x8A:
|
||||
case 0x8B:
|
||||
case 0x8C:
|
||||
case 0x8D:
|
||||
case 0x9C:
|
||||
case 0x9D:
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
var next2 = (int)cmd[i++];
|
||||
// Interpreting
|
||||
public static string[] ParseScript( uint[] cmd, int sanity = -1 )
|
||||
{
|
||||
// sub_148CBC Moon v1.0
|
||||
List<string> parse = new List<string>();
|
||||
int sanityMode = 0;
|
||||
|
||||
op = EA(c, next1, next2);
|
||||
break;
|
||||
}
|
||||
string ErrorNear( int line, string error )
|
||||
{
|
||||
var start = Math.Max( line - 6, 0 );
|
||||
var end = Math.Min( line + 6, cmd.Length - 1 );
|
||||
var toPrint = cmd.Skip( start ).Take( end - start );
|
||||
var message = $"Error at line {line}:" + Environment.NewLine;
|
||||
|
||||
case 0x8E:
|
||||
case 0x8F:
|
||||
case 0x90:
|
||||
case 0x91:
|
||||
{
|
||||
var next1 = cmd[i++];
|
||||
var next2 = cmd[i++];
|
||||
var next3 = cmd[i++];
|
||||
message += string.Join( " ", toPrint.Select( b => $"{b:X2}" ) ) + Environment.NewLine;
|
||||
|
||||
op = EF(c, next1, next2, next3);
|
||||
break;
|
||||
}
|
||||
for ( var x = 0; x < line - start; x++ )
|
||||
message += " ";
|
||||
|
||||
case 0x92:
|
||||
case 0x93:
|
||||
case 0x94:
|
||||
case 0x95:
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
var next2 = (int)cmd[i++];
|
||||
var next3 = (int)cmd[i++];
|
||||
var next4 = (int)cmd[i++];
|
||||
message += "^^" + Environment.NewLine;
|
||||
message += error;
|
||||
|
||||
op = EA(c, next1, next2, next3, next4);
|
||||
break;
|
||||
}
|
||||
return message;
|
||||
}
|
||||
|
||||
case 0x96: // float
|
||||
case 0x97:
|
||||
case 0x98:
|
||||
case 0x99:
|
||||
{
|
||||
var next1 = cmd[i++];
|
||||
var next2 = cmd[i++];
|
||||
var next3 = cmd[i++];
|
||||
var next4 = cmd[i++];
|
||||
var next5 = cmd[i++];
|
||||
int i = 0; // Current Offset of decompressed instructions
|
||||
while ( i < cmd.Length ) // read away
|
||||
{
|
||||
// Read a Command
|
||||
|
||||
op = EF(c, next1, next2, next3, next4, next5);
|
||||
break;
|
||||
}
|
||||
string instrLine;
|
||||
var line = i;
|
||||
var opcodeval = cmd[ i++ ];
|
||||
var opcodesafe = opcodeval & 0xFFFF;
|
||||
|
||||
case 0x9A:
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
var next2 = (int)cmd[i++];
|
||||
// a bunch of sanity checking
|
||||
if ( !Enum.IsDefined( typeof( AmxOpCode ), opcodesafe ) )
|
||||
throw new ArgumentException( ErrorNear( line, $"Invalid command ID: {opcodesafe:X4} ({opcodesafe})" ) );
|
||||
|
||||
op = EA(c, next1, next2);
|
||||
break;
|
||||
}
|
||||
var opcode = (AmxOpCode) opcodesafe;
|
||||
|
||||
case 0x9B: // Copy
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
var next2 = (int)cmd[i++];
|
||||
// a bunch of sanity checking
|
||||
if ( !OpCodeTypes.TryGetValue( opcode, out var optype ) )
|
||||
throw new ArgumentException( ErrorNear( line, $"Unknown opcode: {opcodesafe:X4} ({opcodesafe})" ) );
|
||||
|
||||
op = EA(c, next1, next2);
|
||||
break;
|
||||
}
|
||||
switch ( optype )
|
||||
{
|
||||
default:
|
||||
throw new ArgumentException( "Invalid Command Type" );
|
||||
|
||||
case 0x9E:
|
||||
{
|
||||
var next1 = (int)cmd[i++];
|
||||
// perm check a1 + 0x14
|
||||
// can return error code 0x1C
|
||||
case AmxOpCodeType.NoParams:
|
||||
{
|
||||
instrLine = EchoIntCommand( opcode );
|
||||
break;
|
||||
}
|
||||
|
||||
op = EA(c, next1);
|
||||
break;
|
||||
}
|
||||
case AmxOpCodeType.OneParam:
|
||||
{
|
||||
var param = (int) cmd[ i++ ];
|
||||
|
||||
case 0x9F:
|
||||
{
|
||||
// perm check a1 + 0x14
|
||||
// same permission checking as 0x9E
|
||||
// can return error code 0x1C
|
||||
instrLine = EchoIntCommand( opcode, param );
|
||||
break;
|
||||
}
|
||||
|
||||
op = EA(c);
|
||||
break;
|
||||
}
|
||||
case AmxOpCodeType.TwoParams:
|
||||
{
|
||||
var param1 = (int) cmd[ i++ ];
|
||||
var param2 = (int) cmd[ i++ ];
|
||||
|
||||
case 0xA1: // Goto
|
||||
{
|
||||
// minimal sanity checks
|
||||
// can return error code 0x1C
|
||||
int newPos = i + (int)(1 + (2 * (cmd[i] / 4)) + 1);
|
||||
instrLine = EchoIntCommand( opcode, param1, param2 );
|
||||
break;
|
||||
}
|
||||
|
||||
op = EA(c, newPos);
|
||||
break;
|
||||
}
|
||||
case AmxOpCodeType.ThreeParams:
|
||||
{
|
||||
var param1 = (int) cmd[ i++ ];
|
||||
var param2 = (int) cmd[ i++ ];
|
||||
var param3 = (int) cmd[ i++ ];
|
||||
|
||||
case 0xA2: // GetGlobal2
|
||||
case 0xA3: // GetGlobal
|
||||
case 0xA6:
|
||||
case 0xA7:
|
||||
case 0xAF: // SetGlobal
|
||||
case 0xB0:
|
||||
case 0xB3:
|
||||
case 0xB4:
|
||||
case 0xCB:
|
||||
case 0xCD:
|
||||
{
|
||||
// sanity check arg
|
||||
var arg = (short)(c >> 16);
|
||||
instrLine = EchoIntCommand( opcode, param1, param2, param3 );
|
||||
break;
|
||||
}
|
||||
|
||||
op = EA(c & 0xFF, arg);
|
||||
break;
|
||||
}
|
||||
case 0xA4: // GetGlobal4
|
||||
case 0xA5:
|
||||
case 0xA8:
|
||||
case 0xA9:
|
||||
case 0xB1: // SetLocal
|
||||
case 0xB2:
|
||||
case 0xB5:
|
||||
case 0xB6:
|
||||
case 0xCC:
|
||||
case 0xCE:
|
||||
{
|
||||
// sanity check arg, slightly different
|
||||
var arg = (short)(c >> 16);
|
||||
case AmxOpCodeType.FourParams:
|
||||
{
|
||||
var param1 = (int) cmd[ i++ ];
|
||||
var param2 = (int) cmd[ i++ ];
|
||||
var param3 = (int) cmd[ i++ ];
|
||||
var param4 = (int) cmd[ i++ ];
|
||||
|
||||
op = EA(c & 0xFF, arg);
|
||||
break;
|
||||
}
|
||||
}
|
||||
parse.Add($"0x{line * 4:X4}: [{c & 0x7FF:X2}] {op}");
|
||||
}
|
||||
instrLine = EchoIntCommand( opcode, param1, param2, param3, param4 );
|
||||
break;
|
||||
}
|
||||
|
||||
if (sanity >= 0 && sanity != sanityMode)
|
||||
throw new ArgumentException();
|
||||
case AmxOpCodeType.FiveParams:
|
||||
{
|
||||
var param1 = (int) cmd[ i++ ];
|
||||
var param2 = (int) cmd[ i++ ];
|
||||
var param3 = (int) cmd[ i++ ];
|
||||
var param4 = (int) cmd[ i++ ];
|
||||
var param5 = (int) cmd[ i++ ];
|
||||
|
||||
return parse.ToArray();
|
||||
}
|
||||
instrLine = EchoIntCommand( opcode, param1, param2, param3, param4, param5 );
|
||||
break;
|
||||
}
|
||||
|
||||
internal static string[] ParseMovement(uint[] cmd) => Util.GetHexLines(cmd);
|
||||
case AmxOpCodeType.Jump:
|
||||
{
|
||||
var jumpOffset = (int) cmd[ i++ ];
|
||||
var jumpDest = ( line * 4 ) + jumpOffset;
|
||||
|
||||
internal static string EA(uint c, params int[] arr)
|
||||
{
|
||||
string cmd = Commands[c];
|
||||
string parameters = arr.Length == 0 ? "" : string.Join(", ", arr.Select(z => Math.Abs(z) < 100 ? z.ToString() : "0x" + z.ToString("X4")));
|
||||
return $"{cmd}({parameters})";
|
||||
}
|
||||
instrLine = $"{Commands[ opcode ].PadRight( MaxCommandLength, ' ' )} => 0x{jumpDest:X4} ({jumpOffset})";
|
||||
break;
|
||||
}
|
||||
|
||||
private static readonly Func<uint, float> getFloat = val => BitConverter.ToSingle(BitConverter.GetBytes(val), 0);
|
||||
case AmxOpCodeType.Packed:
|
||||
{
|
||||
var param = (short) ( opcodeval >> 16 );
|
||||
|
||||
internal static string EF(uint c, params uint[] arr)
|
||||
{
|
||||
string cmd = Commands[c];
|
||||
string parameters = arr.Length == 1 ? "" : string.Join(", ", arr.Select(getFloat));
|
||||
return $"{cmd}({parameters})";
|
||||
}
|
||||
instrLine = EchoIntCommand( opcode, param );
|
||||
break;
|
||||
}
|
||||
|
||||
private static readonly string[] Commands = Enum.GetNames(typeof(AmxOpCode));
|
||||
}
|
||||
case AmxOpCodeType.CaseTable:
|
||||
{
|
||||
//var jOffset = (i * 4) - 4; // this may be the correct jump start point...
|
||||
var count = cmd[ i++ ]; // switch case table
|
||||
// sanity check
|
||||
|
||||
// Populate Switch-Case Tree
|
||||
var tree = new List<string>();
|
||||
|
||||
// Cases
|
||||
for ( int j = 0; j < count; j++ )
|
||||
{
|
||||
var jmp = (int) cmd[ i++ ];
|
||||
var toOffset = ( ( i - 2 ) * 4 ) + jmp;
|
||||
var ifValue = (int) cmd[ i++ ];
|
||||
tree.Add( $"\t{ifValue} => 0x{toOffset:X4} ({jmp})" );
|
||||
}
|
||||
|
||||
// Default
|
||||
{
|
||||
int jmp = (int) cmd[ i++ ];
|
||||
var toOffset = ( ( i - 2 ) * 4 ) + jmp;
|
||||
tree.Add( $"\t* => 0x{toOffset:X4} ({jmp})" );
|
||||
}
|
||||
|
||||
instrLine = Commands[ opcode ] + Environment.NewLine + string.Join( Environment.NewLine, tree );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( opcode == AmxOpCode.RET || opcode == AmxOpCode.RETN || opcode == AmxOpCode.IRETN )
|
||||
{
|
||||
// Newline after return
|
||||
instrLine += Environment.NewLine;
|
||||
}
|
||||
|
||||
if ( parse.Count == 0 && opcode == AmxOpCode.HALT_P )
|
||||
{
|
||||
// Newline after 0x0000 HALT.P
|
||||
instrLine += Environment.NewLine;
|
||||
}
|
||||
|
||||
parse.Add( $"0x{line * 4:X4}: [{opcodeval & 0x7FF:X2}] {instrLine}" );
|
||||
}
|
||||
|
||||
if ( sanity >= 0 && sanity != sanityMode )
|
||||
throw new ArgumentException();
|
||||
|
||||
return parse.ToArray();
|
||||
}
|
||||
|
||||
internal static string[] ParseMovement( uint[] cmd ) => Util.GetHexLines( cmd );
|
||||
|
||||
internal static string EchoIntCommand( AmxOpCode c, params int[] arr )
|
||||
{
|
||||
string FormatParameter( int param )
|
||||
{
|
||||
if ( param < -100 || param > 100 )
|
||||
return $"0x{param:X4}";
|
||||
return param.ToString();
|
||||
}
|
||||
|
||||
string commandLeft = Commands[ c ].PadRight( MaxCommandLength, ' ' );
|
||||
string parameters = arr.Length == 0 ? "" : string.Join( ", ", arr.Select( FormatParameter ) );
|
||||
return $"{commandLeft} {parameters}";
|
||||
}
|
||||
|
||||
private static readonly Func<uint, float> getFloat = val => BitConverter.ToSingle( BitConverter.GetBytes( val ), 0 );
|
||||
|
||||
internal static string EchoFloatCommand( AmxOpCode c, params uint[] arr )
|
||||
{
|
||||
string commandLeft = Commands[ c ].PadRight( MaxCommandLength, ' ' );
|
||||
string parameters = arr.Length == 1 ? "" : string.Join( ", ", arr.Select( getFloat ) );
|
||||
return $"{commandLeft} {parameters}";
|
||||
}
|
||||
|
||||
private static readonly Dictionary<AmxOpCode, string> Commands = Enum.GetValues( typeof( AmxOpCode ) )
|
||||
.Cast<AmxOpCode>()
|
||||
.ToDictionary( v => v, v => v.ToString().Replace( '_', '.' ) );
|
||||
|
||||
private static readonly int MaxCommandLength = Commands.Values.Max( cmd => cmd.Length );
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user