cheat-engine/Cheat Engine/disassemblerComments.pas
2014-06-19 09:37:09 +00:00

533 lines
12 KiB
ObjectPascal

unit disassemblerComments;
{
this unit will contain the interface for the disassembler comments
}
{$mode delphi}
interface
uses
windows, Classes, SysUtils, AvgLvlTree, math, cefuncproc, symbolhandler, dom;
type TDisassemblerComments=class
private
commentstree: TAvgLvlTree;
function getCount: integer;
procedure setComment(address: ptruint; comment: string);
function getComment(address: ptruint): string;
procedure setHeader(address: ptruint; header: string);
function getHeader(address: ptruint): string;
procedure setInterpretableAddress(address: ptruint; s: string);
function getInterpretableAddress(address: ptruint): string;
public
procedure clear;
procedure saveToXMLNode(node: TDOMNode);
procedure loadFromXMLNode(node: TDOMNode);
function getCommentInRegion(address: ptruint; size: integer; out actualaddress: ptruint): string;
procedure deleteAddress(address: ptruint);
constructor create;
destructor destroy; override;
property count: integer read getCount;
property comments[address: ptruint]: string read getComment write setComment; default;
property commentHeader[address: ptruint]: string read getHeader write setHeader;
property interpretableAddress[address: ptruint]: string read getInterpretableAddress write setInterpretableAddress; //for saving/loading
end;
var
dassemblercomments: TDisassemblerComments;
procedure reinitializeDisassemblerComments;
implementation
type
PCommentData=^TCommentData;
TCommentData=record
address: ptruint;
interpretableAddress: pchar;
comment: pchar;
header: pchar;
previous: PCommentData;
next: PCommentData;
end;
function TDisassemblerComments.getCount: integer;
begin
result:=commentstree.Count;
end;
procedure TDisassemblerComments.setInterpretableAddress(address: ptruint; s: string);
var search: TCommentData;
c: PCommentData;
n: TAvgLvlTreeNode;
prev,next: TAvgLvlTreeNode;
begin
//check if this comment exists, and if not, add it
search.address:=address;
n:=commentstree.Find(@search);
if n<>nil then
begin
c:=n.data;
if c.interpretableAddress<>nil then
StrDispose(c.interpretableAddress);
c.interpretableAddress:=strnew(pchar(s));
end;
end;
function TDisassemblerComments.getInterpretableAddress(address: ptruint): string;
var
search: TCommentData;
n: TAvgLvlTreeNode;
begin
zeromemory(@search, sizeof(search));
search.address:=address;
n:=commentstree.Find(@search);
if n<>nil then
result:=PCommentData(n.data).interpretableAddress
else
result:='';
end;
procedure TDisassemblerComments.setComment(address: ptruint; comment: string);
var search: TCommentData;
c: PCommentData;
n: TAvgLvlTreeNode;
prev,next: TAvgLvlTreeNode;
mi: TModuleInfo;
begin
//check if this comment exists, and if not, add it
ZeroMemory(@search, sizeof(search));
search.address:=address;
n:=commentstree.Find(@search);
if n=nil then //create the node
begin
if comment='' then exit; //don't add it
c:=getmem(sizeof(TCommentData));
ZeroMemory(c, sizeof(TCommentData));
c.address:=address;
c.comment:=strnew(pchar(comment));
c.header:=nil;
if symhandler.getmodulebyaddress(address, mi) then
c.interpretableAddress:=strnew(pchar('"'+mi.modulename+'"+'+inttohex(address-mi.baseaddress,1) ))
else
c.interpretableAddress:=strnew(pchar(inttohex(address,8) ));
n:=commentstree.Add(c);
prev:=commentstree.FindPrecessor(n);
next:=commentstree.FindSuccessor(n);
if prev=nil then
c.previous:=nil
else
begin
c.previous:=prev.Data;
PCommentData(prev.data).next:=c;
end;
if next=nil then
c.next:=nil
else
begin
c.next:=next.Data;
PCommentData(next.data).previous:=c;
end;
end
else
begin
//update
c:=n.data;
if (c.header=nil) and (comment='') then
begin
deleteAddress(address);
exit;
end;
if c.comment<>nil then
StrDispose(c.comment);
if comment<>'' then
c.comment:=strnew(pchar(comment))
else
c.comment:=nil;
end;
end;
function TDisassemblerComments.getComment(address: ptruint): string;
var
search: TCommentData;
n: TAvgLvlTreeNode;
begin
zeromemory(@search, sizeof(search));
search.address:=address;
n:=commentstree.Find(@search);
if (n<>nil) and (n.Data<>nil) and (PCommentData(n.data).comment<>nil) then
result:=PCommentData(n.data).comment
else
result:='';
end;
procedure TDisassemblerComments.setHeader(address: ptruint; header: string);
var search: TCommentData;
c: PCommentData;
n: TAvgLvlTreeNode;
prev,next: TAvgLvlTreeNode;
mi: TModuleInfo;
begin
//check if this comment exists, and if not, add it
ZeroMemory(@search, sizeof(search));
search.address:=address;
n:=commentstree.Find(@search);
if n=nil then //create the node
begin
if header='' then exit;
c:=getmem(sizeof(TCommentData));
ZeroMemory(c, sizeof(TCommentData));
c.address:=address;
c.comment:=nil;
c.header:=strnew(pchar(header));
if symhandler.getmodulebyaddress(address, mi) then
c.interpretableAddress:=strnew(pchar('"'+mi.modulename+'"+'+inttohex(address-mi.baseaddress,1) ))
else
c.interpretableAddress:=strnew(pchar(inttohex(address,8) ));
n:=commentstree.Add(c);
prev:=commentstree.FindPrecessor(n);
next:=commentstree.FindSuccessor(n);
if prev=nil then
c.previous:=nil
else
begin
c.previous:=prev.Data;
PCommentData(prev.data).next:=c;
end;
if next=nil then
c.next:=nil
else
begin
c.next:=next.Data;
PCommentData(next.data).previous:=c;
end;
end
else
begin
//update
c:=n.data;
if (c.comment=nil) and (header='') then
begin
deleteAddress(address);
exit;
end;
if c.header<>nil then
StrDispose(c.header);
if header<>'' then
c.header:=strnew(pchar(header))
else
c.header:=nil;
end;
end;
function TDisassemblerComments.getHeader(address: ptruint): string;
var
search: TCommentData;
n: TAvgLvlTreeNode;
begin
zeromemory(@search, sizeof(search));
search.address:=address;
n:=commentstree.Find(@search);
if (n<>nil) and (n.data<>nil) and (PCommentData(n.data).header<>nil) then
result:=PCommentData(n.data).header
else
result:='';
end;
function TDisassemblerComments.getCommentInRegion(address: ptruint; size: integer; out actualaddress: ptruint): string;
var
search: TCommentData;
n: TAvgLvlTreeNode;
c: PCommentData;
begin
result:='';
actualaddress:=0;
search.address:=address;
n:=commentstree.FindNearest(@search);
if n<>nil then
begin
c:=n.data;
if c.address>=address+size then
c:=c.previous;
if (c<>nil) and (c.address<address) then
c:=c.next;
//now check if c fits in the region, if not, not found
if InRangeX(c.address, address , address+size-1) then
begin
actualaddress:=c.address;
if c.comment<>nil then
result:=c.comment
else
result:='';
end;
end;
end;
procedure TDisassemblerComments.deleteAddress(address: ptruint);
var
search: TCommentData;
n: TAvgLvlTreeNode;
begin
search.address:=address;
n:=commentstree.Find(@search);
if n<>nil then
begin
if PCommentData(n.data).comment<>nil then
StrDispose(PCommentData(n.data).comment);
if PCommentData(n.data).header<>nil then
StrDispose(PCommentData(n.data).header);
freemem(n.data);
commentstree.Delete(n);
end;
end;
procedure TDisassemblerComments.loadFromXMLNode(node: TDOMNode);
var i: integer;
addressstring: string;
address: ptruint;
commentstring: string;
headerstring: string;
n,n2: TDomnode;
e: boolean;
begin
clear;
commentstring:='';
headerstring:='';
for i:=0 to node.ChildNodes.Count-1 do
begin
n:=node.ChildNodes[i];
if n.NodeName='DisassemblerComment' then
begin
n2:=n.FindNode('Address');
if n2<>nil then
begin
addressstring:=n2.TextContent;
n2:=n.FindNode('Comment');
if n2<>nil then
commentstring:=n2.TextContent;
n2:=n.FindNode('Header');
if n2<>nil then
headerstring:=n2.TextContent;
address:=symhandler.getAddressFromName(addressstring, false, e);
if e then
address:=i+1; //as a temp holder
if commentstring<>'' then
comments[address]:=commentstring;
if headerstring<>'' then
commentHeader[address]:=headerstring;
interpretableAddress[address]:=addressstring;
end;
end;
end;
end;
procedure TDisassemblerComments.saveToXMLNode(node: TDOMNode);
var n: TAvgLvlTreeNode;
c: PCommentData;
doc: TDOMDocument;
commentnode: TDOMNode;
begin
doc:=node.OwnerDocument;
n:=commentstree.FindLowest;
if n<>nil then
begin
c:=n.data;
while c<>nil do
begin
commentnode:=node.AppendChild(doc.CreateElement('DisassemblerComment'));
commentnode.AppendChild(doc.CreateElement('Address')).TextContent:=c.interpretableAddress;
if c.comment<>nil then
commentnode.AppendChild(doc.CreateElement('Comment')).TextContent:=c.comment;
if c.header<>nil then
commentnode.AppendChild(doc.CreateElement('Header')).TextContent:=c.header;
c:=c.next;
end;
end;
end;
function Compare(Item1, Item2: Pointer): Integer;
begin
result:=comparevalue(PCommentData(item1).address, PCommentData(item2).address);
end;
procedure TDisassemblercomments.clear;
var
c, prev: PCommentData;
n: TAvgLvlTreeNode;
begin
if commentstree<>nil then
begin
//free the comments and the comment data objects
n:=commentstree.FindLowest;
if n<>nil then
begin
c:=n.Data;
while c<>nil do
begin
if c.comment<>nil then
begin
StrDispose(c.comment);
c.comment:=nil;
end;
if c.header<>nil then
begin
StrDispose(c.header);
c.header:=nil;
end;
if c.interpretableAddress<>nil then
begin
strdispose(c.interpretableAddress);
c.interpretableAddress:=nil;
end;
prev:=c;
c:=c.next;
freemem(prev);
end;
end;
commentstree.Clear;
end;
end;
constructor TDisassemblerComments.create;
begin
commentstree:=TAvgLvlTree.Create(Compare);
end;
destructor TDisassemblerComments.destroy;
begin
clear;
freeandnil(commentstree);
inherited destroy;
end;
procedure reinitializeDisassemblerComments;
var newcomments, olddassemblercomments: TDisassemblerComments;
i: integer;
n: TAvgLvlTreeNode;
c: PCommentData;
e: boolean;
address: ptruint;
comment, header: string;
begin
newcomments:=TDisassemblerComments.create;
n:=dassemblercomments.commentstree.FindLowest;
if n<>nil then
begin
c:=n.data;
while c<>nil do
begin
address:=symhandler.getAddressFromName(c.interpretableAddress, true, e);
if e then //couldn't get resolved
address:=c.address;
if c.comment<>nil then
comment:=c.comment
else
comment:='';
if c.header<>nil then
header:=c.header
else
header:='';
newcomments.comments[address]:=comment;
newcomments.commentHeader[address]:=header;
newcomments.interpretableAddress[address]:=c.interpretableAddress; //force the interpretable address in case it failed setting
c:=c.next;
end;
end;
olddassemblercomments:=dassemblercomments;
dassemblercomments:=newcomments;
olddassemblercomments.Free;
end;
initialization
dassemblercomments:=TDisassemblerComments.create;
end.