Simba/Units/PascalScript/uPSDisassembly.pas

500 lines
15 KiB
Plaintext

unit uPSDisassembly;
{$I PascalScript.inc}
interface
uses
uPSRuntime, uPSUtils, sysutils;
function IFPS3DataToText(const Input: tbtstring; var Output: string): Boolean;
implementation
type
TMyPSExec = class(TPSExec)
function ImportProc(const Name: ShortString; proc: TIFExternalProcRec): Boolean; override;
end;
function Debug2Str(const s: string): string;
var
i: Integer;
begin
result := '';
for i := 1 to length(s) do
begin
if (s[i] < #32) or (s[i] > #128) then
result := result + '\'+inttohex(ord(s[i]), 2)
else if s[i] = '\' then
result := result + '\\'
else
result := result + s[i];
end;
end;
function SpecImportProc(Sender: TObject; p: TIFExternalProcRec): Boolean; forward;
function FloatToStr(Value: Extended): string;
begin
try
Result := SysUtils.FloatToStr(Value);
except
Result := 'NaNa';
end;
end;
function IFPS3DataToText(const Input: tbtstring; var Output: string): Boolean;
var
I: TMyPSExec;
procedure Writeln(const s: string);
begin
Output := Output + s + #13#10;
end;
function BT2S(P: PIFTypeRec): string;
var
i: Longint;
begin
case p.BaseType of
btU8: Result := 'U8';
btS8: Result := 'S8';
btU16: Result := 'U16';
btS16: Result := 'S16';
btU32: Result := 'U32';
btS32: Result := 'S32';
{$IFNDEF PS_NOINT64}bts64: Result := 'S64'; {$ENDIF}
btChar: Result := {$IFDEF UNICODE}'AnsiChar'{$ELSE}'Char'{$ENDIF};
{$IFNDEF PS_NOWIDESTRING}
btWideChar: Result := 'WideChar';
btWideString: Result := 'WideString';
{$ENDIF}
btSet: Result := 'Set';
btSingle: Result := 'Single';
btDouble: Result := 'Double';
btExtended: Result := 'Extended';
btString: Result := 'String';
btRecord:
begin
Result := 'Record(';
for i := 0 to TPSTypeRec_Record(p).FieldTypes.Count-1 do
begin
if i <> 0 then Result := Result+',';
Result := Result + BT2S(PIFTypeRec(TPSTypeRec_Record(p).FieldTypes[i]));
end;
Result := Result + ')';
end;
btArray: Result := 'Array of '+BT2S(TPSTypeRec_Array(p).ArrayType);
btResourcePointer: Result := 'ResourcePointer';
btPointer: Result := 'Pointer';
btVariant: Result := 'Variant';
btClass: Result := 'Class';
btProcPtr: Result := 'ProcPtr';
btStaticArray: Result := 'StaticArray['+inttostR(TPSTypeRec_StaticArray(p).Size)+'] of '+BT2S(TPSTypeRec_Array(p).ArrayType);
else
Result := 'Unknown '+inttostr(p.BaseType);
end;
end;
procedure WriteTypes;
var
T: Longint;
begin
Writeln('[TYPES]');
for T := 0 to i.FTypes.Count -1 do
begin
if PIFTypeRec(i.FTypes[t]).ExportName <> '' then
Writeln('Type ['+inttostr(t)+']: '+bt2s(PIFTypeRec(i.FTypes[t]))+' Export: '+PIFTypeRec(i.FTypes[t]).ExportName)
else
Writeln('Type ['+inttostr(t)+']: '+bt2s(PIFTypeRec(i.FTypes[t])));
end;
end;
procedure WriteVars;
var
T: Longint;
function FindType(p: Pointer): Cardinal;
var
T: Longint;
begin
Result := Cardinal(-1);
for T := 0 to i.FTypes.Count -1 do
begin
if p = i.FTypes[t] then begin
result := t;
exit;
end;
end;
end;
begin
Writeln('[VARS]');
for t := 0 to i.FGlobalVars.count -1 do
begin
Writeln('Var ['+inttostr(t)+']: '+ IntToStr(FindType(PIFVariant(i.FGlobalVars[t])^.FType)) + ' '+ bt2s(PIFVariant(i.FGlobalVars[t])^.Ftype) + ' '+ PIFVariant(i.FGlobalVars[t])^.Ftype.ExportName);
end;
end;
procedure WriteProcs;
var
t: Longint;
procedure WriteProc(proc: TPSProcRec);
var
sc, CP: Cardinal;
function ReadData(var Data; Len: Cardinal): Boolean;
begin
if CP + Len <= TPSInternalProcRec(PROC).Length then begin
Move(TPSInternalProcRec(Proc).Data[CP], Data, Len);
CP := CP + Len;
Result := True;
end else Result := False;
end;
function ReadByte(var B: Byte): Boolean;
begin
if CP < TPSInternalProcRec(Proc).Length then begin
b := TPSInternalProcRec(Proc).Data^[cp];
Inc(CP);
Result := True;
end else Result := False;
end;
function ReadLong(var B: Cardinal): Boolean;
begin
if CP + 3 < TPSInternalProcRec(Proc).Length then begin
b := Cardinal((@TPSInternalProcRec(Proc).Data[CP])^);
Inc(CP, 4);
Result := True;
end else Result := False;
end;
function ReadWriteVariable: string;
var
VarType: byte;
L1, L2: Cardinal;
function ReadVar(FType: Cardinal): string;
var
F: PIFTypeRec;
b: byte;
w: word;
l: Cardinal;
{$IFNDEF PS_NOINT64}ff: Int64;{$ENDIF}
e: extended;
ss: single;
d: double;
s: ansistring;
c: char;
{$IFNDEF PS_NOWIDESTRING}
wc: WideChar;
ws: WideString;
{$ENDIF}
begin
result := '';
F:= i.FTypes[Ftype];
if f = nil then exit;
case f.BaseType of
btProcPtr: begin if not ReadData(l, 4) then exit; Result := 'PROC: '+inttostr(l); end;
btU8: begin if not ReadData(b, 1) then exit; Result := IntToStr(tbtu8(B)); end;
btS8: begin if not ReadData(b, 1) then exit; Result := IntToStr(tbts8(B)); end;
btU16: begin if not ReadData(w, 2) then exit; Result := IntToStr(tbtu16(w)); end;
btS16: begin if not ReadData(w, 2) then exit; Result := IntToStr(tbts16(w)); end;
btU32: begin if not ReadData(l, 4) then exit; Result := IntToStr(tbtu32(l)); end;
btS32: begin if not ReadData(l, 4) then exit; Result := IntToStr(tbts32(l)); end;
{$IFNDEF PS_NOINT64}bts64: begin if not ReadData(ff, 8) then exit; Result := IntToStr(ff); end;{$ENDIF}
btSingle: begin if not ReadData(ss, Sizeof(tbtsingle)) then exit; Result := FloatToStr(ss); end;
btDouble: begin if not ReadData(d, Sizeof(tbtdouble)) then exit; Result := FloatToStr(d); end;
btExtended: begin if not ReadData(e, Sizeof(tbtextended)) then exit; Result := FloatToStr(e); end;
btPChar, btString: begin if not ReadData(l, 4) then exit; SetLength(s, l); if not readData(s[1], l) then exit; Result := MakeString(s); end;
btSet:
begin
SetLength(s, TPSTypeRec_Set(f).aByteSize);
if not ReadData(s[1], length(s)) then exit;
result := MakeString(s);
end;
btChar: begin if not ReadData(c, 1) then exit; Result := '#'+IntToStr(ord(c)); end;
{$IFNDEF PS_NOWIDESTRING}
btWideChar: begin if not ReadData(wc, 2) then exit; Result := '#'+IntToStr(ord(wc)); end;
btWideString: begin if not ReadData(l, 4) then exit; SetLength(ws, l); if not readData(ws[1], l*2) then exit; Result := MakeWString(ws); end;
{$ENDIF}
end;
end;
function AddressToStr(a: Cardinal): String;
begin
if a < PSAddrNegativeStackStart then
Result := 'GlobalVar['+inttostr(a)+']'
else
Result := 'Base['+inttostr(Longint(A-PSAddrStackStart))+']';
end;
begin
Result := '';
if not ReadByte(VarType) then Exit;
case VarType of
0:
begin
if not ReadLong(L1) then Exit;
Result := AddressToStr(L1);
end;
1:
begin
if not ReadLong(L1) then Exit;
Result := '['+ReadVar(l1)+']';
end;
2:
begin
if not ReadLong(L1) then Exit;
if not ReadLong(L2) then Exit;
Result := AddressToStr(L1)+'.['+inttostr(l2)+']';
end;
3:
begin
if not ReadLong(l1) then Exit;
if not ReadLong(l2) then Exit;
Result := AddressToStr(L1)+'.'+AddressToStr(l2);
end;
end;
end;
var
b: Byte;
s: string;
DP, D1, D2, d3, d4: Cardinal;
begin
CP := 0;
sc := 0;
while true do
begin
DP := cp;
if not ReadByte(b) then Exit;
case b of
CM_A:
begin
{$IFDEF FPC}
Output := Output + ' ['+inttostr(dp)+'] ASSIGN '+ ReadWriteVariable;
Output := Output + ', ' + ReadWriteVariable + #13#10;
{$ELSE}
Writeln(' ['+inttostr(dp)+'] ASSIGN '+ReadWriteVariable+ ', ' + ReadWriteVariable);
{$ENDIF}
end;
CM_CA:
begin
if not ReadByte(b) then exit;
case b of
0: s:= '+';
1: s := '-';
2: s := '*';
3: s:= '/';
4: s:= 'MOD';
5: s:= 'SHL';
6: s:= 'SHR';
7: s:= 'AND';
8: s:= 'OR';
9: s:= 'XOR';
else
exit;
end;
Writeln(' ['+inttostr(dp)+'] CALC '+ReadWriteVariable+ ' '+s+' ' + ReadWriteVariable);
end;
CM_P:
begin
Inc(sc);
Writeln(' ['+inttostr(dp)+'] PUSH '+ReadWriteVariable + ' // '+inttostr(sc));
end;
CM_PV:
begin
Inc(sc);
Writeln(' ['+inttostr(dp)+'] PUSHVAR '+ReadWriteVariable + ' // '+inttostr(sc));
end;
CM_PO:
begin
Dec(Sc);
Writeln(' ['+inttostr(dp)+'] POP // '+inttostr(sc));
end;
Cm_C:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] CALL '+inttostr(d1));
end;
Cm_PG:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] POP/GOTO currpos + '+IntToStr(d1)+' ['+IntToStr(CP+d1)+']');
end;
Cm_P2G:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] POP2/GOTO currpos + '+IntToStr(d1)+' ['+IntToStr(CP+d1)+']');
end;
Cm_G:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] GOTO currpos + '+IntToStr(d1)+' ['+IntToStr(CP+d1)+']');
end;
Cm_CG:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] COND_GOTO currpos + '+IntToStr(d1)+' '+ReadWriteVariable+' ['+IntToStr(CP+d1)+']');
end;
Cm_CNG:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] COND_NOT_GOTO currpos + '+IntToStr(d1)+' '+ReadWriteVariable+' ['+IntToStr(CP+d1)+']');
end;
Cm_R: Writeln(' ['+inttostr(dp)+'] RET');
Cm_ST:
begin
if not ReadLong(d1) or not readLong(d2) then exit;
Writeln(' ['+inttostr(dp)+'] SETSTACKTYPE Base['+inttostr(d1)+'] '+inttostr(d2));
end;
Cm_Pt:
begin
Inc(sc);
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] PUSHTYPE '+inttostr(d1) + '('+BT2S(TPSTypeRec(I.FTypes[d1]))+') // '+inttostr(sc));
end;
CM_CO:
begin
if not readByte(b) then exit;
case b of
0: s := '>=';
1: s := '<=';
2: s := '>';
3: s := '<';
4: s := '<>';
5: s := '=';
else exit;
end;
Writeln(' ['+inttostr(dp)+'] COMPARE into '+ReadWriteVariable+': '+ReadWriteVariable+' '+s+' '+ReadWriteVariable);
end;
Cm_cv:
begin
Writeln(' ['+inttostr(dp)+'] CALLVAR '+ReadWriteVariable);
end;
Cm_inc:
begin
Writeln(' ['+inttostr(dp)+'] INC '+ReadWriteVariable);
end;
Cm_dec:
begin
Writeln(' ['+inttostr(dp)+'] DEC '+ReadWriteVariable);
end;
cm_sp:
begin
Writeln(' ['+inttostr(dp)+'] SETPOINTER '+ReadWriteVariable+': '+ReadWriteVariable);
end;
cm_spc:
begin
Writeln(' ['+inttostr(dp)+'] SETCOPYPOINTER '+ReadWriteVariable+': '+ReadWriteVariable);
end;
cm_in:
begin
Writeln(' ['+inttostr(dp)+'] INOT '+ReadWriteVariable);
end;
cm_bn:
begin
Writeln(' ['+inttostr(dp)+'] BNOT '+ReadWriteVariable);
end;
cm_vm:
begin
Writeln(' ['+inttostr(dp)+'] MINUS '+ReadWriteVariable);
end;
cm_sf:
begin
s := ReadWriteVariable;
if not ReadByte(b) then exit;
if b = 0 then
Writeln(' ['+inttostr(dp)+'] SETFLAG '+s)
else
Writeln(' ['+inttostr(dp)+'] SETFLAG NOT '+s);
end;
cm_fg:
begin
if not ReadLong(D1) then exit;
Writeln(' ['+inttostr(dp)+'] FLAGGOTO currpos + '+IntToStr(d1)+' ['+IntToStr(CP+d1)+']');
end;
cm_puexh:
begin
if not ReadLong(D1) then exit;
if not ReadLong(D2) then exit;
if not ReadLong(D3) then exit;
if not ReadLong(D4) then exit;
Writeln(' ['+inttostr(dp)+'] PUSHEXCEPTION '+inttostr(d1)+' '+inttostr(d2)+' '+inttostr(d3)+' '+inttostr(d4));
end;
cm_poexh:
begin
if not ReadByte(b) then exit;
Writeln(' ['+inttostr(dp)+'] POPEXCEPTION '+inttostr(b));
end;
else
begin
Writeln(' Disasm Error');
Break;
end;
end;
end;
end;
begin
Writeln('[PROCS]');
for t := 0 to i.FProcs.Count -1 do
begin
if TPSProcRec(i.FProcs[t]).ClassType = TIFExternalProcRec then
begin
if TPSExternalProcRec(i.FProcs[t]). Decl = '' then
Writeln('Proc ['+inttostr(t)+']: External: '+TPSExternalProcRec(i.FProcs[t]).Name)
else
Writeln('Proc ['+inttostr(t)+']: External Decl: '+Debug2Str(TIFExternalProcRec(i.FProcs[t]).Decl) + ' ' + TIFExternalProcRec(i.FProcs[t]).Name);
end else begin
if TPSInternalProcRec(i.FProcs[t]).ExportName <> '' then
begin
Writeln('Proc ['+inttostr(t)+'] Export: '+TPSInternalProcRec(i.FProcs[t]).ExportName+' '+TPSInternalProcRec(i.FProcs[t]).ExportDecl);
end else
Writeln('Proc ['+inttostr(t)+']');
Writeproc(i.FProcs[t]);
end;
end;
end;
begin
Result := False;
try
I := TMyPSExec.Create;
I.AddSpecialProcImport('', @SpecImportProc, nil);
if not I.LoadData(Input) then begin
I.Free;
Exit;
end;
Output := '';
WriteTypes;
WriteVars;
WriteProcs;
I.Free;
except
exit;
end;
result := true;
end;
{ TMyIFPSExec }
function MyDummyProc(Caller: TPSExec; p: TIFExternalProcRec; Global, Stack: TPSStack): Boolean;
begin
Result := False;
end;
function TMyPSExec.ImportProc(const Name: ShortString;
proc: TIFExternalProcRec): Boolean;
begin
Proc.ProcPtr := MyDummyProc;
result := true;
end;
function SpecImportProc(Sender: TObject; p: TIFExternalProcRec): Boolean;
begin
p.ProcPtr := MyDummyProc;
Result := True;
end;
end.