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.addressnil 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.