2595 lines
69 KiB
ObjectPascal
2595 lines
69 KiB
ObjectPascal
unit MemoryRecordUnit;
|
|
|
|
{$mode DELPHI}
|
|
|
|
interface
|
|
|
|
{$ifdef windows}
|
|
uses
|
|
Windows, forms, graphics, Classes, SysUtils, controls, stdctrls, comctrls,symbolhandler,
|
|
cefuncproc,newkernelhandler, autoassembler, hotkeyhandler, dom, XMLRead,XMLWrite,
|
|
customtypehandler, fileutil, LCLProc, commonTypeDefs, pointerparser, LazUTF8, LuaClass;
|
|
{$endif}
|
|
|
|
{$ifdef unix}
|
|
//only used as a class to store entries and freeze/setvalue. It won't have a link with the addresslist and does not decide it's position
|
|
uses
|
|
unixporthelper, Classes, sysutils, symbolhandler, NewKernelHandler, DOM,
|
|
XMLRead, XMLWrite, CustomTypeHandler, FileUtil, commonTypeDefs, math, pointerparser;
|
|
{$endif}
|
|
|
|
resourcestring
|
|
rsMRNibbleSupportIsOnlyForHexadecimalDisplay = 'Nibble support is only for hexadecimal display';
|
|
rsPqqqqqqqq = 'P->????????';
|
|
rsP = 'P->';
|
|
rsError = 'error';
|
|
|
|
type TMemrecHotkeyAction=(mrhToggleActivation, mrhToggleActivationAllowIncrease, mrhToggleActivationAllowDecrease, mrhActivate, mrhDeactivate, mrhSetValue, mrhIncreaseValue, mrhDecreaseValue);
|
|
|
|
type TFreezeType=(ftFrozen, ftAllowIncrease, ftAllowDecrease);
|
|
|
|
|
|
|
|
type TMemrecOption=(moHideChildren, moActivateChildrenAsWell, moDeactivateChildrenAsWell, moRecursiveSetValue, moAllowManualCollapseAndExpand, moManualExpandCollapse);
|
|
type TMemrecOptions=set of TMemrecOption;
|
|
|
|
type TMemrecStringData=record
|
|
unicode: boolean;
|
|
length: integer;
|
|
ZeroTerminate: boolean;
|
|
end;
|
|
|
|
type TMemRecBitData=record
|
|
Bit : Byte;
|
|
bitlength: integer;
|
|
showasbinary: boolean;
|
|
end;
|
|
|
|
type TMemRecByteData=record
|
|
bytelength: integer;
|
|
end;
|
|
|
|
type TMemRecAutoAssemblerData=record
|
|
script: tstringlist;
|
|
allocs: TCEAllocArray;
|
|
registeredsymbols: TStringlist;
|
|
end;
|
|
|
|
type TMemRecExtraData=record
|
|
case integer of
|
|
1: (stringData: TMemrecStringData); //if this is the last level (maxlevel) this is an PPointerList
|
|
2: (bitData: TMemRecBitData); //else it's a PReversePointerListArray
|
|
3: (byteData: TMemRecByteData);
|
|
end;
|
|
|
|
|
|
type
|
|
TMemoryRecordHotkey=class;
|
|
TMemoryRecord=class;
|
|
|
|
TMemrecOffset=class
|
|
private
|
|
fowner: TMemoryRecord;
|
|
foffset: integer;
|
|
special: boolean; //if set, look at luaref or text, else just keep it to offset (also, update offset to the latest value while at it)
|
|
text: string; //symhandler interpretable value, or a luastatement
|
|
luaref: integer; //if lua, this contains a reference to the function (so it doesn't have to be parsed each time)
|
|
function getOffsetNoBase: integer;
|
|
procedure cleanupluaref;
|
|
public
|
|
function getOffset(currentBase: ptruint): integer;
|
|
procedure setOffset(o: integer);
|
|
function setOffsetText(s: string): boolean;
|
|
constructor create(owner: TMemoryRecord);
|
|
destructor destroy; override;
|
|
published
|
|
property offset: integer read getOffsetNoBase write setOffset;
|
|
|
|
end;
|
|
|
|
TMemrecOffsetList=array of TMemrecOffset;
|
|
|
|
|
|
TMemoryRecordActivateEvent=function (sender: TObject; before, currentstate: boolean): boolean of object;
|
|
|
|
TMemoryRecord=class
|
|
private
|
|
fID: integer;
|
|
FrozenValue : string;
|
|
CurrentValue: string;
|
|
UndoValue : string; //keeps the last value before a manual edit
|
|
|
|
|
|
UnreadablePointer: boolean;
|
|
BaseAddress: ptrUint; //Base address
|
|
RealAddress: ptrUint; //If pointer, or offset the real address
|
|
fIsOffset: boolean;
|
|
|
|
|
|
fShowAsSignedOverride: boolean;
|
|
fShowAsSigned: boolean;
|
|
|
|
fActive: boolean;
|
|
fAllowDecrease: boolean;
|
|
fAllowIncrease: boolean;
|
|
fOwner: TObject;
|
|
|
|
fShowAsHex: boolean;
|
|
editcount: integer; //=0 when not being edited
|
|
|
|
fOptions: TMemrecOptions;
|
|
|
|
CustomType: TCustomType;
|
|
fCustomTypeName: string;
|
|
fColor: TColor;
|
|
fVisible: boolean;
|
|
|
|
fVarType : TVariableType;
|
|
|
|
couldnotinterpretaddress: boolean; //set when the address interpetation has failed since last eval
|
|
|
|
hknameindex: integer;
|
|
|
|
Hotkeylist: tlist;
|
|
fisGroupHeader: Boolean; //set if it's a groupheader, only the description matters then
|
|
fIsReadableAddress: boolean;
|
|
|
|
fDropDownList: Tstringlist;
|
|
fDropDownReadOnly: boolean;
|
|
fDropDownDescriptionOnly: boolean;
|
|
fDisplayAsDropDownListItem: boolean;
|
|
|
|
fDontSave: boolean;
|
|
|
|
luaref: integer; //luaclass object to this instance
|
|
|
|
fonactivate, fondeactivate: TMemoryRecordActivateEvent;
|
|
fOnDestroy: TNotifyEvent;
|
|
|
|
fpointeroffsets: array of TMemrecOffset; //if longer than 0, this is a pointer
|
|
function getPointerOffset(index: integer): TMemrecOffset;
|
|
|
|
function getByteSize: integer;
|
|
function BinaryToString(b: pbytearray; bufsize: integer): string;
|
|
function getAddressString: string;
|
|
function getuniquehotkeyid: integer;
|
|
procedure setActive(state: boolean);
|
|
procedure setAllowDecrease(state: boolean);
|
|
procedure setAllowIncrease(state: boolean);
|
|
procedure setVisible(state: boolean);
|
|
procedure setShowAsHex(state: boolean);
|
|
procedure setOptions(newOptions: TMemrecOptions);
|
|
procedure setCustomTypeName(name: string);
|
|
procedure setColor(c: TColor);
|
|
procedure setVarType(v: TVariableType);
|
|
function getHotkeyCount: integer;
|
|
function getHotkey(index: integer): TMemoryRecordHotkey;
|
|
function GetshowAsSigned: boolean;
|
|
procedure setShowAsSigned(state: boolean);
|
|
|
|
|
|
function getChildCount: integer;
|
|
function getChild(index: integer): TMemoryRecord;
|
|
|
|
|
|
|
|
procedure setID(i: integer);
|
|
function getIndex: integer;
|
|
function getParent: TMemoryRecord;
|
|
|
|
function getDropDownCount: integer;
|
|
function getDropDownValue(index: integer): string;
|
|
function getDropDownDescription(index: integer): string;
|
|
|
|
function GetCollapsed: boolean;
|
|
procedure SetCollapsed(state: boolean);
|
|
public
|
|
|
|
|
|
|
|
|
|
Description : string;
|
|
interpretableaddress: string;
|
|
|
|
|
|
|
|
|
|
Extra: TMemRecExtraData;
|
|
AutoAssemblerData: TMemRecAutoAssemblerData;
|
|
|
|
{$ifndef unix}
|
|
treenode: TTreenode;
|
|
autoAssembleWindow: TForm; //window storage for an auto assembler editor window
|
|
{$endif}
|
|
|
|
isSelected: boolean; //lazarus bypass. Because lazarus does not implement multiselect I have to keep track of which entries are selected
|
|
|
|
//showAsHex: boolean;
|
|
|
|
//free for editing by user:
|
|
function hasSelectedParent: boolean;
|
|
function hasParent: boolean;
|
|
|
|
|
|
function isBeingEdited: boolean;
|
|
procedure beginEdit;
|
|
procedure endEdit;
|
|
|
|
procedure setOffsetCount(c: integer);
|
|
function getoffsetCount: integer;
|
|
function isPointer: boolean;
|
|
function isOffset: boolean;
|
|
procedure ApplyFreeze;
|
|
|
|
function GetDisplayValue: string;
|
|
function GetValue: string;
|
|
procedure SetValue(v: string); overload;
|
|
procedure SetValue(v: string; isFreezer: boolean); overload;
|
|
procedure UndoSetValue;
|
|
function canUndo: boolean;
|
|
procedure increaseValue(value: string);
|
|
procedure decreaseValue(value: string);
|
|
function GetRealAddress: PtrUInt;
|
|
function getBaseAddress: ptrUint; //return the base address, if offset, the calculated address
|
|
procedure RefreshCustomType;
|
|
function ReinterpretAddress(forceremovalofoldaddress: boolean=false): boolean;
|
|
//property Value: string read GetValue write SetValue;
|
|
property bytesize: integer read getByteSize;
|
|
|
|
function hasHotkeys: boolean;
|
|
|
|
function Addhotkey(keys: tkeycombo; action: TMemrecHotkeyAction; value, description: string): TMemoryRecordHotkey;
|
|
function removeHotkey(hk: TMemoryRecordHotkey): boolean;
|
|
|
|
procedure DoHotkey(hk :TMemoryRecordHotkey); //execute the specific hotkey action
|
|
|
|
|
|
procedure disablewithoutexecute;
|
|
procedure refresh;
|
|
|
|
procedure getXMLNode(node: TDOMNode; selectedOnly: boolean);
|
|
procedure setXMLnode(CheatEntry: TDOMNode);
|
|
|
|
function getCurrentDropDownIndex: integer;
|
|
|
|
procedure SetVisibleChildrenState;
|
|
|
|
procedure cleanupPointerOffsets;
|
|
function getLuaRef: integer;
|
|
|
|
constructor Create(AOwner: TObject);
|
|
destructor destroy; override;
|
|
|
|
|
|
property HotkeyCount: integer read getHotkeyCount;
|
|
property Hotkey[index: integer]: TMemoryRecordHotkey read getHotkey;
|
|
|
|
property visible: boolean read fVisible write setVisible;
|
|
|
|
property Child[index: integer]: TMemoryRecord read getChild; default;
|
|
|
|
|
|
|
|
published
|
|
property IsGroupHeader: boolean read fisGroupHeader write fisGroupHeader;
|
|
property IsReadableAddress: boolean read fIsReadableAddress; //gets set by getValue, so at least read the value once
|
|
property IsReadable: boolean read fIsReadableAddress;
|
|
property ID: integer read fID write setID;
|
|
property Index: integer read getIndex;
|
|
property Collapsed: boolean read GetCollapsed write SetCollapsed;
|
|
property Color: TColor read fColor write setColor;
|
|
property Count: integer read getChildCount;
|
|
property AddressString: string read getAddressString;
|
|
property Active: boolean read fActive write setActive;
|
|
property VarType: TVariableType read fVarType write setVarType;
|
|
property CustomTypeName: string read fCustomTypeName write setCustomTypeName;
|
|
property Value: string read GetValue write SetValue;
|
|
property DisplayValue: string read GetDisplayValue;
|
|
property DontSave: boolean read fDontSave write fDontSave;
|
|
property AllowDecrease: boolean read fallowDecrease write setAllowDecrease;
|
|
property AllowIncrease: boolean read fallowIncrease write setAllowIncrease;
|
|
property ShowAsHex: boolean read fShowAsHex write setShowAsHex;
|
|
property ShowAsSigned: boolean read getShowAsSigned write setShowAsSigned;
|
|
property Options: TMemrecOptions read fOptions write setOptions;
|
|
property DropDownList: TStringlist read fDropDownList;
|
|
property DropDownReadOnly: boolean read fDropDownReadOnly write fDropDownReadOnly;
|
|
property DropDownDescriptionOnly: boolean read fDropDownDescriptionOnly write fDropDownDescriptionOnly;
|
|
property DisplayAsDropDownListItem: boolean read fDisplayAsDropDownListItem write fDisplayAsDropDownListItem;
|
|
property DropDownCount: integer read getDropDownCount;
|
|
property DropDownValue[index:integer]: string read getDropDownValue;
|
|
property DropDownDescription[index:integer]: string read getDropDownDescription;
|
|
property Parent: TMemoryRecord read getParent;
|
|
property OnActivate: TMemoryRecordActivateEvent read fOnActivate write fOnActivate;
|
|
property OnDeactivate: TMemoryRecordActivateEvent read fOnDeActivate write fOndeactivate;
|
|
property OnDestroy: TNotifyEvent read fOnDestroy write fOnDestroy;
|
|
property offsetCount: integer read getoffsetCount write setOffsetCount;
|
|
property offsets[index: integer]: TMemrecOffset read getPointerOffset;
|
|
end;
|
|
|
|
TMemoryRecordHotkey=class
|
|
private
|
|
fOnHotkey: TNotifyevent;
|
|
fOnPostHotkey: TNotifyevent;
|
|
factivateSound: string;
|
|
fdeactivateSound: string;
|
|
public
|
|
fID: integer;
|
|
fDescription: string;
|
|
fOwner: TMemoryRecord;
|
|
keys: Tkeycombo;
|
|
action: TMemrecHotkeyAction;
|
|
value: string;
|
|
|
|
|
|
|
|
procedure doHotkey;
|
|
constructor create(AnOwner: TMemoryRecord);
|
|
destructor destroy; override;
|
|
published
|
|
property ActivateSound: string read factivateSound write factivateSound;
|
|
property DeactivateSound: string read fdeactivateSound write fdeactivateSound;
|
|
property Description: string read fDescription;
|
|
property Owner: TMemoryRecord read fOwner;
|
|
property ID: integer read fID;
|
|
property OnHotkey: TNotifyEvent read fOnHotkey write fOnHotkey;
|
|
property OnPostHotkey: TNotifyEvent read fOnPostHotkey write fOnPostHotkey;
|
|
end;
|
|
|
|
|
|
function MemRecHotkeyActionToText(action: TMemrecHotkeyAction): string;
|
|
function TextToMemRecHotkeyAction(text: string): TMemrecHotkeyAction;
|
|
|
|
implementation
|
|
|
|
{$ifdef windows}
|
|
uses mainunit, addresslist, formsettingsunit, LuaHandler, lua, lauxlib, lualib,
|
|
processhandlerunit, Parsers;
|
|
{$endif}
|
|
|
|
{$ifdef unix}
|
|
uses processhandlerunit, Parsers;
|
|
{$endif}
|
|
|
|
{-----------------------------TMemrecOffset---------------------------------}
|
|
|
|
function TMemrecOffset.getOffsetNoBase: integer;
|
|
begin
|
|
result:=getOffset(0);
|
|
end;
|
|
|
|
function TMemrecOffset.getOffset(currentBase: ptruint): integer;
|
|
var
|
|
e: boolean;
|
|
memrecluaobjectref: integer;
|
|
stack: integer;
|
|
begin
|
|
if special then
|
|
begin
|
|
foffset:=0;
|
|
|
|
//parse it/call the lua function
|
|
if luaref=-1 then
|
|
foffset:=symhandler.getAddressFromName(text, false, e)
|
|
else
|
|
begin
|
|
memrecluaobjectref:=fowner.getLuaRef;
|
|
lua_rawgeti(Luavm, LUA_REGISTRYINDEX, memrecluaobjectref);
|
|
lua_pushinteger(luavm, currentBase);
|
|
|
|
LUACS.Enter;
|
|
try
|
|
stack:=lua_Gettop(luavm);
|
|
|
|
if lua_pcall(Luavm, 2, 1,0)=0 then
|
|
foffset:=lua_tointeger(Luavm, -1);
|
|
|
|
finally
|
|
lua_settop(luavm, stack);
|
|
luacs.Leave;
|
|
end;
|
|
|
|
|
|
end;
|
|
end;
|
|
result:=foffset;
|
|
end;
|
|
|
|
procedure TMemrecOffset.cleanupluaref;
|
|
begin
|
|
if luaref<>-1 then //dereference this lua function
|
|
begin
|
|
luaL_unref(LuaVM, LUA_REGISTRYINDEX, luaref);
|
|
luaref:=-1;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemrecOffset.setOffset(o: integer);
|
|
begin
|
|
special:=false;
|
|
foffset:=o;
|
|
end;
|
|
|
|
function TMemrecOffset.setOffsetText(s: string): boolean;
|
|
var
|
|
e: boolean;
|
|
ft: tstringlist;
|
|
stack: integer;
|
|
begin
|
|
cleanupluaref;
|
|
|
|
special:=not TryStrToInt('$'+s,foffset);
|
|
result:=special;
|
|
|
|
if special then
|
|
begin
|
|
text:=s;
|
|
//parse it as a symbolhandler text, if that fails, try lua
|
|
|
|
foffset:=symhandler.getAddressFromName(s, false, e);
|
|
if e then
|
|
begin
|
|
//try lua
|
|
ft:=tstringlist.create;
|
|
ft.add('memrec, address=...');
|
|
ft.add('return '+s);
|
|
|
|
|
|
LUACS.Enter;
|
|
try
|
|
stack:=lua_Gettop(luavm);
|
|
if luaL_loadstring(luavm, pchar(ft.text))=0 then
|
|
if lua_isfunction(luavm,-1) then //store a reference to this function
|
|
luaref:=luaL_ref(luavm, LUA_REGISTRYINDEX);
|
|
finally
|
|
lua_settop(luavm, stack);
|
|
LuaCS.Leave;
|
|
end;
|
|
|
|
result:=luaref<>-1;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
constructor TMemrecOffset.create(owner: TMemoryRecord);
|
|
begin
|
|
fOwner:=owner;
|
|
luaref:=-1;
|
|
end;
|
|
|
|
destructor TMemrecOffset.destroy;
|
|
begin
|
|
cleanupluaref;
|
|
end;
|
|
|
|
|
|
{-----------------------------TMemoryRecordHotkey------------------------------}
|
|
constructor TMemoryRecordHotkey.create(AnOwner: TMemoryRecord);
|
|
begin
|
|
//add to the hotkeylist
|
|
fid:=-1;
|
|
fowner:=AnOwner;
|
|
fowner.hotkeylist.Add(self);
|
|
|
|
keys[0]:=0;
|
|
{$ifdef windows}
|
|
RegisterHotKey2(mainform.handle, 0, keys, self);
|
|
{$endif}
|
|
|
|
end;
|
|
|
|
destructor TMemoryRecordHotkey.destroy;
|
|
begin
|
|
{$ifdef windows}
|
|
UnregisterAddressHotkey(self);
|
|
{$endif}
|
|
|
|
//remove this hotkey from the memoryrecord
|
|
if owner<>nil then
|
|
owner.hotkeylist.Remove(self);
|
|
end;
|
|
|
|
procedure TMemoryRecordHotkey.doHotkey;
|
|
begin
|
|
if assigned(fonhotkey) then
|
|
fOnHotkey(self);
|
|
|
|
if owner<>nil then //just be safe (e.g other app sending message)
|
|
owner.DoHotkey(self);
|
|
|
|
if assigned(fonPostHotkey) then
|
|
fOnPostHotkey(self);
|
|
end;
|
|
|
|
{---------------------------------MemoryRecord---------------------------------}
|
|
|
|
function TMemoryRecord.GetCollapsed: boolean;
|
|
begin
|
|
result:=not treenode.Expanded;
|
|
end;
|
|
|
|
procedure TMemoryRecord.SetCollapsed(state: boolean);
|
|
begin
|
|
if state then
|
|
treenode.Collapse(false)
|
|
else
|
|
treenode.Expand(false);
|
|
end;
|
|
|
|
|
|
function TMemoryRecord.getDropDownCount: integer;
|
|
begin
|
|
result:=fDropDownList.count;
|
|
end;
|
|
|
|
function TMemoryRecord.getDropDownValue(index: integer): string;
|
|
begin
|
|
result:='';
|
|
if index<DropDownCount then
|
|
result:=copy(fDropDownList[index], 1, pos(':', fDropDownList[index])-1);
|
|
end;
|
|
|
|
function TMemoryRecord.getDropDownDescription(index: integer): string;
|
|
begin
|
|
result:='';
|
|
if index<DropDownCount then
|
|
result:=copy(fDropDownList[index], pos(':', fDropDownList[index])+1, length(fDropDownList[index]));
|
|
end;
|
|
|
|
function TMemoryRecord.getCurrentDropDownIndex: integer;
|
|
var i: integer;
|
|
begin
|
|
result:=-1;
|
|
for i:=0 to DropDownCount-1 do
|
|
begin
|
|
if lowercase(Value)=lowercase(DropDownValue[i]) then
|
|
result:=i;
|
|
end;
|
|
|
|
end;
|
|
|
|
function TMemoryRecord.getChildCount: integer;
|
|
begin
|
|
result:=0;
|
|
{$ifndef unix}
|
|
if treenode<>nil then
|
|
result:=treenode.Count;
|
|
{$endif}
|
|
end;
|
|
|
|
function TMemoryRecord.getChild(index: integer): TMemoryRecord;
|
|
begin
|
|
|
|
{$IFNDEF UNIX}
|
|
if index<Count then
|
|
result:=TMemoryRecord(treenode.Items[index].Data)
|
|
else
|
|
{$ENDIF}
|
|
result:=nil;
|
|
end;
|
|
|
|
function TMemoryRecord.getHotkeyCount: integer;
|
|
begin
|
|
result:=hotkeylist.count;
|
|
end;
|
|
|
|
function TMemoryRecord.getHotkey(index: integer): TMemoryRecordHotkey;
|
|
begin
|
|
result:=nil;
|
|
|
|
if index<hotkeylist.count then
|
|
result:=TMemoryRecordHotkey(hotkeylist[index]);
|
|
end;
|
|
|
|
procedure TMemoryRecord.cleanupPointerOffsets;
|
|
var i: integer;
|
|
begin
|
|
for i:=0 to length(fpointeroffsets)-1 do
|
|
if fpointeroffsets[i]<>nil then
|
|
freeandnil(fpointeroffsets[i]);
|
|
|
|
setlength(fpointeroffsets,0);
|
|
end;
|
|
|
|
function TMemoryRecord.getLuaRef: integer;
|
|
begin
|
|
if luaref=-1 then
|
|
begin
|
|
luaclass_newClass(luavm, self);
|
|
luaref:=luaL_ref(luavm, LUA_REGISTRYINDEX);
|
|
end;
|
|
|
|
result:=luaref;
|
|
end;
|
|
|
|
constructor TMemoryRecord.create(AOwner: TObject);
|
|
begin
|
|
fVisible:=true;
|
|
fid:=-1;
|
|
fOwner:=AOwner;
|
|
fColor:=clWindowText;
|
|
|
|
hotkeylist:=tlist.create;
|
|
fDropDownList:=tstringlist.create;
|
|
|
|
foptions:=[];
|
|
|
|
luaref:=-1;
|
|
|
|
inherited create;
|
|
end;
|
|
|
|
destructor TMemoryRecord.destroy;
|
|
var i: integer;
|
|
begin
|
|
if assigned(fOnDestroy) then
|
|
fOnDestroy(self);
|
|
|
|
//unregister hotkeys
|
|
if hotkeylist<>nil then
|
|
begin
|
|
while hotkeylist.count>0 do
|
|
TMemoryRecordHotkey(hotkeylist[0]).free;
|
|
|
|
hotkeylist.free;
|
|
end;
|
|
|
|
//free script space
|
|
if autoassemblerdata.script<>nil then
|
|
autoassemblerdata.script.free;
|
|
|
|
//free script info
|
|
if autoassemblerdata.registeredsymbols<>nil then
|
|
autoassemblerdata.registeredsymbols.free;
|
|
|
|
//free the group's children
|
|
{$IFNDEF UNIX}
|
|
while (treenode.count>0) do
|
|
TMemoryRecord(treenode[0].data).free;
|
|
|
|
|
|
if treenode<>nil then
|
|
treenode.free;
|
|
{$ENDIF}
|
|
|
|
if fDropDownList<>nil then
|
|
freeandnil(fDropDownList);
|
|
|
|
if luaref<>-1 then
|
|
luaL_unref(LuaVM, LUA_REGISTRYINDEX, luaref);
|
|
|
|
inherited Destroy;
|
|
|
|
end;
|
|
|
|
|
|
|
|
procedure TMemoryRecord.SetVisibleChildrenState;
|
|
{Called when options change and when children are assigned}
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
if (not factive) and (moHideChildren in foptions) then
|
|
treenode.Collapse(true)
|
|
else
|
|
treenode.Expand(true);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setOptions(newOptions: TMemrecOptions);
|
|
begin
|
|
foptions:=newOptions;
|
|
//apply changes (moHideChildren, moBindActivation, moRecursiveSetValue)
|
|
SetVisibleChildrenState;
|
|
|
|
refresh;
|
|
end;
|
|
|
|
procedure TMemoryRecord.setCustomTypeName(name: string);
|
|
begin
|
|
fCustomTypeName:=name;
|
|
RefreshCustomType;
|
|
end;
|
|
|
|
procedure TMemoryRecord.setVarType(v: TVariableType);
|
|
begin
|
|
//setup some of the default settings
|
|
case v of
|
|
vtUnicodeString: //this type was added later. convert it to a string
|
|
begin
|
|
fvartype:=vtString;
|
|
extra.stringData.unicode:=true;
|
|
extra.stringData.ZeroTerminate:=true;
|
|
end;
|
|
|
|
vtPointer: //also added later. In this case show as a hex value
|
|
begin
|
|
if processhandler.is64bit then
|
|
fvartype:=vtQword
|
|
else
|
|
fvartype:=vtDword;
|
|
|
|
showAsHex:=true;
|
|
end;
|
|
|
|
|
|
vtString: //if setting to the type of string enable the zero terminate method by default
|
|
extra.stringData.ZeroTerminate:=true;
|
|
|
|
vtAutoAssembler:
|
|
if AutoAssemblerData.script=nil then
|
|
AutoAssemblerData.script:=tstringlist.create;
|
|
end;
|
|
|
|
|
|
|
|
fVarType:=v;
|
|
end;
|
|
|
|
procedure TMemoryRecord.setColor(c: TColor);
|
|
begin
|
|
fColor:=c;
|
|
{$IFNDEF UNIX}
|
|
TAddresslist(fOwner).Update;
|
|
{$ENDIF}
|
|
|
|
end;
|
|
|
|
procedure TMemoryRecord.setXMLnode(CheatEntry: TDOMNode);
|
|
var
|
|
tempnode,tempnode2: TDOMNode;
|
|
i,j,k,l: integer;
|
|
|
|
currentEntry: TDOMNode;
|
|
|
|
hk: TMemoryRecordHotkey;
|
|
memrec: TMemoryRecord;
|
|
a:TDOMNode;
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
if TDOMElement(CheatEntry).TagName<>'CheatEntry' then exit; //invalid node type
|
|
|
|
tempnode:=Cheatentry.FindNode('ID');
|
|
if tempnode<>nil then
|
|
id:=strtoint(tempnode.textcontent);
|
|
|
|
tempnode:=CheatEntry.FindNode('Description');
|
|
if tempnode<>nil then
|
|
Description:=ansitoutf8(tempnode.TextContent);
|
|
|
|
if (description<>'') and ((description[1]='"') and (description[length(description)]='"')) then
|
|
description:=copy(description,2,length(description)-2);
|
|
|
|
|
|
tempnode:=CheatEntry.FindNode('Options');
|
|
if tempnode<>nil then
|
|
begin
|
|
if tempnode.HasAttributes then
|
|
begin
|
|
a:=tempnode.Attributes.GetNamedItem('moHideChildren');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moHideChildren];
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moBindActivation'); //support for loading older tables that use this
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
begin
|
|
foptions:=foptions+[moActivateChildrenAsWell];
|
|
foptions:=foptions+[moDeactivateChildrenAsWell];
|
|
end;
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moActivateChildrenAsWell');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moActivateChildrenAsWell];
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moDeactivateChildrenAsWell');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moDeactivateChildrenAsWell];
|
|
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moRecursiveSetValue');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moRecursiveSetValue];
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moAllowManualCollapseAndExpand');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moAllowManualCollapseAndExpand];
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('moManualExpandCollapse');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
foptions:=foptions+[moManualExpandCollapse];
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
tempnode:=CheatEntry.FindNode('DropDownList');
|
|
if tempnode<>nil then
|
|
begin
|
|
fDropDownList.Text:=tempnode.textcontent;
|
|
|
|
if tempnode.HasAttributes then
|
|
begin
|
|
a:=tempnode.Attributes.GetNamedItem('DescriptionOnly');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
DropDownDescriptionOnly:=true;
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('ReadOnly');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
DropDownReadOnly:=true;
|
|
|
|
a:=tempnode.Attributes.GetNamedItem('DisplayValueAsItem');
|
|
if (a<>nil) and (a.TextContent='1') then
|
|
DisplayAsDropDownListItem:=true;
|
|
end;
|
|
|
|
end;
|
|
|
|
tempnode:=CheatEntry.FindNode('ShowAsHex');
|
|
if tempnode<>nil then
|
|
fshowashex:=tempnode.textcontent='1';
|
|
|
|
tempnode:=CheatEntry.FindNode('ShowAsSigned');
|
|
if tempnode<>nil then
|
|
begin
|
|
fShowAsSignedOverride:=true;
|
|
fShowAsSigned:=tempnode.textcontent='1';
|
|
end;
|
|
|
|
|
|
tempnode:=CheatEntry.FindNode('Color');
|
|
if tempnode<>nil then
|
|
begin
|
|
try
|
|
fColor:=strtoint('$'+tempnode.textcontent);
|
|
except
|
|
end;
|
|
end;
|
|
|
|
tempnode:=CheatEntry.FindNode('GroupHeader');
|
|
if tempnode<>nil then
|
|
begin
|
|
fisGroupHeader:=tempnode.TextContent='1';
|
|
end;
|
|
|
|
|
|
tempnode:=CheatEntry.FindNode('CheatEntries');
|
|
if tempnode<>nil then
|
|
begin
|
|
currentEntry:=tempnode.FirstChild;
|
|
while currentEntry<>nil do
|
|
begin
|
|
//create a blank entry
|
|
memrec:=TMemoryRecord.create(fOwner);
|
|
|
|
memrec.treenode:=treenode.owner.AddObject(nil,'',memrec);
|
|
memrec.treenode.MoveTo(treenode, naAddChild); //make it the last child of this node
|
|
|
|
|
|
//fill the entry with the node info
|
|
memrec.setXMLnode(currentEntry);
|
|
currentEntry:=currentEntry.NextSibling;
|
|
end;
|
|
|
|
end;
|
|
|
|
treenode.Expand(true);
|
|
|
|
|
|
|
|
begin
|
|
tempnode:=CheatEntry.FindNode('VariableType');
|
|
if tempnode<>nil then
|
|
VarType:=StringToVariableType(tempnode.TextContent);
|
|
|
|
case VarType of
|
|
vtCustom:
|
|
begin
|
|
tempnode:=CheatEntry.FindNode('CustomType');
|
|
if tempnode<>nil then
|
|
setCustomTypeName(tempnode.TextContent);
|
|
end;
|
|
|
|
vtBinary:
|
|
begin
|
|
tempnode:=CheatEntry.FindNode('BitStart');
|
|
if tempnode<>nil then
|
|
extra.bitData.Bit:=strtoint(tempnode.TextContent);
|
|
|
|
tempnode:=CheatEntry.FindNode('BitLength');
|
|
if tempnode<>nil then
|
|
extra.bitData.bitlength:=strtoint(tempnode.TextContent);
|
|
|
|
tempnode:=CheatEntry.FindNode('ShowAsBinary');
|
|
if tempnode<>nil then
|
|
extra.bitData.ShowAsBinary:=tempnode.TextContent='1';
|
|
end;
|
|
|
|
vtString:
|
|
begin
|
|
tempnode:=CheatEntry.FindNode('Length');
|
|
if tempnode<>nil then
|
|
extra.stringData.length:=strtoint(tempnode.TextContent);
|
|
|
|
tempnode:=CheatEntry.FindNode('Unicode');
|
|
if tempnode<>nil then
|
|
extra.stringData.Unicode:=tempnode.TextContent='1';
|
|
|
|
tempnode:=CheatEntry.FindNode('ZeroTerminate');
|
|
if tempnode<>nil then
|
|
extra.stringdata.ZeroTerminate:=tempnode.TextContent='1';
|
|
end;
|
|
|
|
vtByteArray:
|
|
begin
|
|
tempnode:=CheatEntry.FindNode('ByteLength');
|
|
if tempnode<>nil then
|
|
extra.byteData.bytelength:=strtoint(tempnode.TextContent);
|
|
end;
|
|
|
|
vtAutoAssembler:
|
|
begin
|
|
tempnode:=Cheatentry.FindNode('AssemblerScript');
|
|
|
|
if tempnode<>nil then
|
|
begin
|
|
if AutoAssemblerData.script<>nil then
|
|
freeAndNil(AutoAssemblerData.script);
|
|
|
|
setlength(AutoAssemblerData.allocs,0);
|
|
if AutoAssemblerData.registeredsymbols<>nil then
|
|
freeandnil(AutoAssemblerData.registeredsymbols);
|
|
|
|
AutoAssemblerData.script:=tstringlist.Create;
|
|
AutoAssemblerData.script.text:=tempnode.TextContent;
|
|
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
tempnode:=CheatEntry.FindNode('Address');
|
|
if tempnode<>nil then
|
|
interpretableaddress:=tempnode.TextContent;
|
|
|
|
|
|
tempnode:=CheatEntry.FindNode('Offsets');
|
|
if tempnode<>nil then
|
|
begin
|
|
offsetCount:=tempnode.ChildNodes.Count;
|
|
|
|
j:=0;
|
|
for i:=0 to tempnode.ChildNodes.Count-1 do
|
|
begin
|
|
if tempnode.ChildNodes[i].NodeName='Offset' then
|
|
begin
|
|
fpointeroffsets[j].offset:=strtoint('$'+tempnode.ChildNodes[i].TextContent);
|
|
inc(j);
|
|
end;
|
|
end;
|
|
|
|
offsetcount:=j; //set to the proper size
|
|
end;
|
|
|
|
tempnode:=CheatEntry.FindNode('Hotkeys');
|
|
|
|
if tempnode<>nil then
|
|
begin
|
|
while hotkeycount>0 do //erase the old hotkey list
|
|
hotkey[0].free;
|
|
|
|
|
|
for i:=0 to tempnode.ChildNodes.count-1 do
|
|
begin
|
|
hk:=TMemoryRecordHotkey.Create(self);
|
|
|
|
if tempnode.ChildNodes[i].NodeName='Hotkey' then
|
|
begin
|
|
hk.value:='';
|
|
ZeroMemory(@hk.keys,sizeof(TKeyCombo));
|
|
|
|
tempnode2:=tempnode.childnodes[i].FindNode('Description');
|
|
if tempnode2<>nil then
|
|
hk.fdescription:=tempnode2.textcontent;
|
|
|
|
tempnode2:=tempnode.childnodes[i].FindNode('ID');
|
|
|
|
if tempnode2<>nil then
|
|
hk.fid:=strtoint(tempnode2.textcontent);
|
|
|
|
|
|
tempnode2:=tempnode.childnodes[i].FindNode('Action');
|
|
if tempnode2<>nil then
|
|
hk.action:=TextToMemRecHotkeyAction(tempnode2.TextContent);
|
|
|
|
tempnode2:=tempnode.childnodes[i].findnode('Value');
|
|
if tempnode2<>nil then
|
|
hk.value:=tempnode2.TextContent;
|
|
|
|
tempnode2:=tempnode.childnodes[i].findnode('ActivateSound');
|
|
if tempnode2<>nil then
|
|
hk.activateSound:=tempnode2.TextContent;
|
|
|
|
tempnode2:=tempnode.childnodes[i].findnode('DeactivateSound');
|
|
if tempnode2<>nil then
|
|
hk.deactivateSound:=tempnode2.TextContent;
|
|
|
|
tempnode2:=tempnode.ChildNodes[i].FindNode('Keys');
|
|
if tempnode2<>nil then
|
|
begin
|
|
l:=0;
|
|
for k:=0 to tempnode2.ChildNodes.Count-1 do
|
|
begin
|
|
if tempnode2.ChildNodes[k].NodeName='Key' then
|
|
begin
|
|
try
|
|
hk.keys[l]:=StrToInt(tempnode2.ChildNodes[k].TextContent);
|
|
inc(l);
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
//check if a hotkey has an id, and if not create one for it
|
|
for i:=0 to HotkeyCount-1 do
|
|
if hotkey[i].id=-1 then
|
|
hotkey[i].fid:=getuniquehotkeyid;
|
|
end;
|
|
ReinterpretAddress;
|
|
refresh;
|
|
end;
|
|
|
|
|
|
SetVisibleChildrenState;
|
|
{$ENDIF}
|
|
|
|
|
|
end;
|
|
|
|
function TMemoryRecord.getParent: TMemoryRecord;
|
|
{$IFNDEF UNIX}
|
|
var tn: TTreenode;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
result:=nil;
|
|
tn:=treenode.parent;
|
|
if tn<>nil then
|
|
result:=TMemoryRecord(tn.data);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMemoryRecord.hasParent: boolean;
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
result:=treenode.parent<>nil;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMemoryRecord.hasSelectedParent: boolean;
|
|
{$IFNDEF UNIX}
|
|
var tn: TTreenode;
|
|
m: TMemoryRecord;
|
|
{$ENDIF}
|
|
begin
|
|
//if this node has a direct parent that is selected it returns true, else it will ask the parent if that one has a selected parent etc... untill there is no more parent, or one is selected
|
|
{$IFNDEF UNIX}
|
|
result:=false;
|
|
tn:=treenode.Parent;
|
|
if tn<>nil then
|
|
begin
|
|
m:=TMemoryRecord(tn.data);
|
|
if m.isSelected then
|
|
result:=true
|
|
else
|
|
result:=m.hasSelectedParent;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.getXMLNode(node: TDOMNode; selectedOnly: boolean);
|
|
{$IFNDEF UNIX}
|
|
var
|
|
doc: TDOMDocument;
|
|
cheatEntry: TDOMNode;
|
|
cheatEntries: TDOMNode;
|
|
offsets: TDOMNode;
|
|
hks, hk,hkkc: TDOMNode;
|
|
opt: TDOMNode;
|
|
laststate: TDOMNode;
|
|
|
|
tn: TTreenode;
|
|
i,j: integer;
|
|
a:TDOMAttr;
|
|
|
|
s: ansistring;
|
|
|
|
ddl: TDOMNode;
|
|
{$ENDIF}
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
if selectedonly then
|
|
begin
|
|
if (not isselected) then exit; //don't add if not selected and only the selected items should be added
|
|
|
|
//it is selected, check if it has a parent that is selected, if not, continue, else exit
|
|
if hasSelectedParent then exit;
|
|
end
|
|
else
|
|
if fDontSave then exit; //don't save this and it's children if it's not a selection copy (and if it is a selection, don't copy the fDontSave)
|
|
|
|
|
|
doc:=node.OwnerDocument;
|
|
cheatEntry:=doc.CreateElement('CheatEntry');
|
|
cheatEntry.AppendChild(doc.CreateElement('ID')).TextContent:=IntToStr(ID);
|
|
|
|
|
|
|
|
s:=utf8tosys(description);
|
|
cheatEntry.AppendChild(doc.CreateElement('Description')).TextContent:='"'+s+'"';
|
|
|
|
//save options
|
|
//(moHideChildren, moBindActivation, moRecursiveSetValue);
|
|
if options<>[] then
|
|
begin
|
|
opt:=cheatEntry.AppendChild(doc.CreateElement('Options'));
|
|
|
|
if moHideChildren in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moHideChildren');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if moActivateChildrenAsWell in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moActivateChildrenAsWell');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if moDeactivateChildrenAsWell in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moDeactivateChildrenAsWell');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if moRecursiveSetValue in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moRecursiveSetValue');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if moAllowManualCollapseAndExpand in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moAllowManualCollapseAndExpand');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if moManualExpandCollapse in options then
|
|
begin
|
|
a:=doc.CreateAttribute('moManualExpandCollapse');
|
|
a.TextContent:='1';
|
|
opt.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
|
|
|
|
|
|
end;
|
|
|
|
if DropDownList.Count>0 then
|
|
begin
|
|
ddl:=cheatEntry.AppendChild(doc.CreateElement('DropDownList'));
|
|
ddl.TextContent:=DropDownList.Text;
|
|
|
|
if DropDownDescriptionOnly then
|
|
begin
|
|
a:=doc.CreateAttribute('DescriptionOnly');
|
|
a.TextContent:='1';
|
|
ddl.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if DropDownReadOnly then
|
|
begin
|
|
a:=doc.CreateAttribute('ReadOnly');
|
|
a.TextContent:='1';
|
|
ddl.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
if DisplayAsDropDownListItem then
|
|
begin
|
|
a:=doc.CreateAttribute('DisplayValueAsItem');
|
|
a.TextContent:='1';
|
|
ddl.Attributes.SetNamedItem(a);
|
|
end;
|
|
end;
|
|
|
|
if Value<>'??' then
|
|
begin
|
|
laststate:=cheatEntry.AppendChild(doc.CreateElement('LastState'));
|
|
if VarType<>vtAutoAssembler then
|
|
begin
|
|
a:=doc.CreateAttribute('RealAddress');
|
|
a.TextContent:=IntToHex(GetRealAddress,8);
|
|
laststate.Attributes.SetNamedItem(a);
|
|
|
|
if VarType<>vtString then
|
|
begin
|
|
a:=doc.CreateAttribute('Value');
|
|
a.TextContent:=value;
|
|
laststate.Attributes.SetNamedItem(a);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
if Active then
|
|
begin
|
|
a:=doc.CreateAttribute('Activated');
|
|
a.TextContent:='1';
|
|
laststate.Attributes.SetNamedItem(a);
|
|
end;
|
|
|
|
|
|
if showAsHex then
|
|
cheatEntry.AppendChild(doc.CreateElement('ShowAsHex')).TextContent:='1';
|
|
|
|
if fShowAsSignedOverride then
|
|
begin
|
|
if fShowAsSigned then
|
|
cheatEntry.AppendChild(doc.CreateElement('ShowAsSigned')).TextContent:='1'
|
|
else
|
|
cheatEntry.AppendChild(doc.CreateElement('ShowAsSigned')).TextContent:='0';
|
|
end;
|
|
|
|
|
|
if fcolor<>clWindowText then
|
|
cheatEntry.AppendChild(doc.CreateElement('Color')).TextContent:=inttohex(fcolor,6);
|
|
|
|
if fisGroupHeader then
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('GroupHeader')).TextContent:='1';
|
|
end
|
|
else
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('VariableType')).TextContent:=VariableTypeToString(vartype);
|
|
case VarType of
|
|
vtCustom:
|
|
begin
|
|
cheatentry.AppendChild(doc.CreateElement('CustomType')).TextContent:=CustomTypeName;
|
|
end;
|
|
|
|
vtBinary:
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('BitStart')).TextContent:=inttostr(extra.bitData.Bit);
|
|
cheatEntry.AppendChild(doc.CreateElement('BitLength')).TextContent:=inttostr(extra.bitData.BitLength);
|
|
cheatEntry.AppendChild(doc.CreateElement('ShowAsBinary')).TextContent:=BoolToStr(extra.bitData.showasbinary,'1','0');
|
|
end;
|
|
|
|
vtString:
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('Length')).TextContent:=inttostr(extra.stringData.length);
|
|
cheatEntry.AppendChild(doc.CreateElement('Unicode')).TextContent:=BoolToStr(extra.stringData.unicode,'1','0');
|
|
cheatEntry.AppendChild(doc.CreateElement('ZeroTerminate')).TextContent:=BoolToStr(extra.stringData.ZeroTerminate,'1','0');
|
|
|
|
end;
|
|
|
|
vtByteArray:
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('ByteLength')).TextContent:=inttostr(extra.byteData.bytelength);
|
|
end;
|
|
|
|
vtAutoAssembler:
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('AssemblerScript')).TextContent:=AutoAssemblerData.script.Text;
|
|
end;
|
|
end;
|
|
|
|
if VarType<>vtAutoAssembler then
|
|
begin
|
|
cheatEntry.AppendChild(doc.CreateElement('Address')).TextContent:=interpretableaddress;
|
|
|
|
if isPointer then
|
|
begin
|
|
Offsets:=cheatEntry.AppendChild(doc.CreateElement('Offsets'));
|
|
|
|
for i:=0 to offsetCount-1 do
|
|
Offsets.AppendChild(doc.CreateElement('Offset')).TextContent:=inttohex(fpointeroffsets[i].offset,1);
|
|
|
|
cheatEntry.AppendChild(Offsets);
|
|
end;
|
|
end;
|
|
|
|
|
|
end;
|
|
|
|
//hotkeys
|
|
|
|
if HotkeyCount>0 then
|
|
begin
|
|
hks:=cheatentry.AppendChild(doc.CreateElement('Hotkeys'));
|
|
for i:=0 to HotkeyCount-1 do
|
|
begin
|
|
hk:=hks.AppendChild(doc.CreateElement('Hotkey'));
|
|
hk.AppendChild(doc.CreateElement('Action')).TextContent:=MemRecHotkeyActionToText(hotkey[i].action);
|
|
hkkc:=hk.AppendChild(doc.createElement('Keys'));
|
|
j:=0;
|
|
while (j<5) and (hotkey[i].keys[j]<>0) do
|
|
begin
|
|
hkkc.appendchild(doc.createElement('Key')).TextContent:=inttostr(hotkey[i].keys[j]);
|
|
inc(j);
|
|
end;
|
|
|
|
if hotkey[i].value<>'' then
|
|
hk.AppendChild(doc.CreateElement('Value')).TextContent:=hotkey[i].value;
|
|
|
|
if hotkey[i].description<>'' then
|
|
hk.AppendChild(doc.CreateElement('Description')).TextContent:=hotkey[i].description;
|
|
|
|
if hotkey[i].id>=0 then
|
|
hk.AppendChild(doc.CreateElement('ID')).TextContent:=inttostr(hotkey[i].id);
|
|
|
|
if hotkey[i].activateSound<>'' then
|
|
hk.AppendChild(doc.CreateElement('ActivateSound')).TextContent:=hotkey[i].activateSound;
|
|
|
|
if hotkey[i].deactivateSound<>'' then
|
|
hk.AppendChild(doc.CreateElement('DeactivateSound')).TextContent:=hotkey[i].deactivateSound;
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
//append the children if it has any
|
|
if treenode.HasChildren then
|
|
begin
|
|
CheatEntries:=doc.CreateElement('CheatEntries');
|
|
tn:=treenode.GetFirstChild;
|
|
while tn<>nil do
|
|
begin
|
|
TMemoryRecord(tn.data).getXMLNode(CheatEntries, false); //take over ALL attached nodes, not just the selected ones
|
|
tn:=tn.GetNextSibling;
|
|
end;
|
|
|
|
cheatentry.AppendChild(CheatEntries);
|
|
end;
|
|
|
|
|
|
node.AppendChild(cheatEntry);
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.refresh;
|
|
begin
|
|
{$IFNDEF UNIX} treenode.Update; {$ENDIF}
|
|
end;
|
|
|
|
|
|
procedure TMemoryRecord.setShowAsSigned(state: boolean);
|
|
begin
|
|
fShowAsSignedOverride:=true;
|
|
fShowAsSigned:=state;
|
|
refresh;
|
|
end;
|
|
|
|
function TMemoryRecord.GetShowAsSigned: boolean;
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
if fShowAsSignedOverride then
|
|
result:=fShowAsSigned
|
|
else
|
|
result:=formSettings.cbShowAsSigned.checked;
|
|
{$ELSE}
|
|
result:=false;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMemoryRecord.isBeingEdited: boolean;
|
|
begin
|
|
result:=editcount>0;
|
|
end;
|
|
|
|
procedure TMemoryRecord.beginEdit;
|
|
begin
|
|
inc(editcount);
|
|
end;
|
|
|
|
procedure TMemoryRecord.endEdit;
|
|
begin
|
|
if editcount>0 then
|
|
dec(editcount);
|
|
end;
|
|
|
|
function TMemoryRecord.getPointerOffset(index: integer): TMemrecOffset;
|
|
begin
|
|
result:=fpointeroffsets[index];
|
|
end;
|
|
|
|
procedure TMemoryRecord.setOffsetCount(c: integer);
|
|
var
|
|
oldc: integer;
|
|
i: integer;
|
|
begin
|
|
oldc:=offsetcount;
|
|
|
|
for i:=oldc-1 downto c do
|
|
freeandnil(fpointeroffsets[i]);
|
|
|
|
setlength(fpointeroffsets, c);
|
|
for i:=oldc to c-1 do
|
|
fpointeroffsets[i]:=TMemrecOffset.create(self);
|
|
end;
|
|
|
|
function TMemoryRecord.getOffsetCount: integer;
|
|
begin
|
|
result:=length(fpointeroffsets);
|
|
end;
|
|
|
|
function TMemoryRecord.isPointer: boolean;
|
|
begin
|
|
result:=offsetcount>0;
|
|
end;
|
|
|
|
function TMemoryRecord.isOffset: boolean;
|
|
begin
|
|
result:=fIsOffset;
|
|
end;
|
|
|
|
function TMemoryRecord.hasHotkeys: boolean;
|
|
begin
|
|
result:=HotkeyCount>0;
|
|
end;
|
|
|
|
function TMemoryRecord.removeHotkey(hk: TMemoryRecordHotkey): boolean;
|
|
begin
|
|
hk.free;
|
|
result:=true;
|
|
end;
|
|
|
|
function TMemoryRecord.getIndex: integer;
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
result:=treenode.AbsoluteIndex;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setID(i: integer);
|
|
{$IFNDEF UNIX}
|
|
var a: TAddresslist;
|
|
{$ENDIF}
|
|
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
if i<>fid then
|
|
begin
|
|
//new id, check fo duplicates (e.g copy/paste)
|
|
a:=TAddresslist(fOwner);
|
|
|
|
if a.getRecordWithID(i)<>nil then
|
|
fid:=a.GetUniqueMemrecId
|
|
else
|
|
fid:=i;
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMemoryRecord.getuniquehotkeyid: integer;
|
|
//goes through the hotkeylist and returns an unused id
|
|
var i: integer;
|
|
isunique: boolean;
|
|
begin
|
|
result:=0;
|
|
for result:=0 to maxint-1 do
|
|
begin
|
|
isunique:=true;
|
|
for i:=0 to hotkeycount-1 do
|
|
if hotkey[i].id=result then
|
|
begin
|
|
isunique:=false;
|
|
break;
|
|
end;
|
|
|
|
if isunique then break;
|
|
end;
|
|
end;
|
|
|
|
function TMemoryRecord.Addhotkey(keys: tkeycombo; action: TMemrecHotkeyAction; value, description: string): TMemoryRecordHotkey;
|
|
{
|
|
adds and registers a hotkey and returns the hotkey index for this hotkey
|
|
return -1 if failure
|
|
}
|
|
var
|
|
hk: TMemoryRecordHotkey;
|
|
begin
|
|
hk:=TMemoryRecordHotkey.create(self);
|
|
|
|
hk.fid:=getuniquehotkeyid;
|
|
hk.keys:=keys;
|
|
hk.action:=action;
|
|
hk.value:=value;
|
|
hk.fdescription:=description;
|
|
|
|
result:=hk;
|
|
end;
|
|
|
|
procedure TMemoryRecord.increaseValue(value: string);
|
|
var
|
|
oldvalue: qword;
|
|
oldvaluedouble: double;
|
|
increasevalue: qword;
|
|
increasevaluedouble: double;
|
|
begin
|
|
if VarType in [vtByte, vtWord, vtDword, vtQword, vtSingle, vtDouble, vtCustom] then
|
|
begin
|
|
try
|
|
if showAsHex then //separate handler for hexadecimal. (handle as int, even for the float types)
|
|
begin
|
|
oldvalue:=StrToQWordEx('$'+getvalue);
|
|
increasevalue:=StrToQwordEx('$'+value);
|
|
setvalue(IntTohex(oldvalue+increasevalue,1));
|
|
end
|
|
else
|
|
begin
|
|
if VarType in [vtByte, vtWord, vtDword, vtQword, vtCustom] then
|
|
begin
|
|
oldvalue:=StrToQWordEx(getvalue);
|
|
increasevalue:=StrToQWordEx(value);
|
|
setvalue(IntToStr(oldvalue+increasevalue));
|
|
end
|
|
else
|
|
begin
|
|
oldvaluedouble:=StrToFloat(getValue);
|
|
increasevalueDouble:=StrToFloat(value);
|
|
setvalue(FloatToStr(oldvaluedouble+increasevalueDouble));
|
|
end;
|
|
end;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemoryRecord.decreaseValue(value: string);
|
|
var
|
|
oldvalue: qword;
|
|
oldvaluedouble: double;
|
|
decreasevalue: qword;
|
|
decreasevaluedouble: double;
|
|
begin
|
|
if VarType in [vtByte, vtWord, vtDword, vtQword, vtSingle, vtDouble] then
|
|
begin
|
|
try
|
|
if showAsHex then //separate handler for hexadecimal. (handle as int, even for the float types)
|
|
begin
|
|
oldvalue:=StrToQWordEx('$'+getvalue);
|
|
decreasevalue:=StrToQwordEx('$'+value);
|
|
setvalue(IntTohex(oldvalue-decreasevalue,1));
|
|
end
|
|
else
|
|
begin
|
|
if VarType in [vtByte, vtWord, vtDword, vtQword] then
|
|
begin
|
|
oldvalue:=StrToQWordEx(getvalue);
|
|
decreasevalue:=StrToQWordEx(value);
|
|
setvalue(IntToStr(oldvalue-decreasevalue));
|
|
end
|
|
else
|
|
begin
|
|
oldvaluedouble:=StrToFloat(getValue);
|
|
decreasevalueDouble:=StrToFloat(value);
|
|
setvalue(FloatToStr(oldvaluedouble-decreasevalueDouble));
|
|
end;
|
|
end;
|
|
except
|
|
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemoryRecord.disablewithoutexecute;
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
factive:=false;
|
|
SetVisibleChildrenState;
|
|
treenode.Update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.DoHotkey(hk: TMemoryRecordhotkey);
|
|
var oldstate: boolean;
|
|
begin
|
|
if (hk<>nil) and (hk.owner=self) then
|
|
begin
|
|
try
|
|
case hk.action of
|
|
mrhToggleActivation:
|
|
begin
|
|
active:=not active;
|
|
|
|
if (hk.activateSound<>'') and active then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
|
|
if (hk.deactivateSound<>'') and (not active) then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.deactivateSound+']]))'); //also gives a signal when failing to activate
|
|
end;
|
|
|
|
mrhSetValue:
|
|
begin
|
|
SetValue(hk.value);
|
|
|
|
if (hk.activateSound<>'') then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
end;
|
|
|
|
mrhIncreaseValue:
|
|
begin
|
|
increaseValue(hk.value);
|
|
if (hk.activateSound<>'') then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
end;
|
|
|
|
mrhDecreaseValue:
|
|
begin
|
|
decreaseValue(hk.value);
|
|
if (hk.activateSound<>'') then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
end;
|
|
|
|
|
|
mrhToggleActivationAllowDecrease:
|
|
begin
|
|
allowDecrease:=True;
|
|
active:=not active;
|
|
|
|
if (hk.activateSound<>'') and active then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
|
|
if (hk.deactivateSound<>'') and (not active) then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.deactivateSound+']]))'); //also gives a signal when failing to activate
|
|
end;
|
|
|
|
mrhToggleActivationAllowIncrease:
|
|
begin
|
|
allowIncrease:=True;
|
|
active:=not active;
|
|
|
|
if (hk.activateSound<>'') and active then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
|
|
if (hk.deactivateSound<>'') and (not active) then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.deactivateSound+']]))'); //also gives a signal when failing to activate
|
|
|
|
end;
|
|
|
|
mrhActivate:
|
|
begin
|
|
active:=true;
|
|
if (hk.activateSound<>'') and active then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.activateSound+']]))');
|
|
end;
|
|
|
|
mrhDeactivate:
|
|
begin
|
|
active:=false;
|
|
if (hk.deactivateSound<>'') and (not active) then
|
|
LUA_DoScript('playSound(findTableFile([['+hk.deactivateSound+']]))'); //also gives a signal when failing to activate
|
|
end;
|
|
|
|
|
|
end;
|
|
except
|
|
//don't complain about incorrect values
|
|
end;
|
|
end;
|
|
|
|
{$IFNDEF UNIX}
|
|
treenode.update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setAllowDecrease(state: boolean);
|
|
begin
|
|
fAllowDecrease:=state;
|
|
if state then
|
|
fAllowIncrease:=false; //at least one of the 2 must always be false
|
|
|
|
{$IFNDEF UNIX}
|
|
treenode.update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setAllowIncrease(state: boolean);
|
|
begin
|
|
fAllowIncrease:=state;
|
|
if state then
|
|
fAllowDecrease:=false; //at least one of the 2 must always be false
|
|
|
|
{$IFNDEF UNIX}
|
|
treenode.update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setActive(state: boolean);
|
|
var f: string;
|
|
i: integer;
|
|
begin
|
|
if state=fActive then exit; //no need to execute this is it's the same state
|
|
outputdebugstring('setting active state with description:'+description+' to '+BoolToStr(state,true));
|
|
|
|
{ deprecated
|
|
|
|
//6.0 compatibility
|
|
if (state) then
|
|
LUA_memrec_callback(self, '_memrec_'+description+'_activating')
|
|
else
|
|
LUA_memrec_callback(self, '_memrec_'+description+'_deactivating');
|
|
}
|
|
|
|
//6.5+
|
|
LUA_functioncall('onMemRecPreExecute',[self, state]);
|
|
|
|
//6.1+
|
|
if state then
|
|
begin
|
|
if assigned(fonactivate) then //activating , before
|
|
if not fonactivate(self, true, fActive) then exit; //do not activate if it returns false
|
|
end
|
|
else
|
|
begin
|
|
if assigned(fondeactivate) then //deactivating , before
|
|
if not fondeactivate(self, true, fActive) then exit; //do not deactivate if it returns false
|
|
end;
|
|
|
|
|
|
if not fisGroupHeader then
|
|
begin
|
|
if self.VarType = vtAutoAssembler then
|
|
begin
|
|
{$IFNDEF UNIX}
|
|
//aa script
|
|
try
|
|
if autoassemblerdata.registeredsymbols=nil then
|
|
autoassemblerdata.registeredsymbols:=tstringlist.create;
|
|
|
|
if autoassemble(autoassemblerdata.script, false, state, false, false, autoassemblerdata.allocs, autoassemblerdata.registeredsymbols) then
|
|
begin
|
|
fActive:=state;
|
|
if autoassemblerdata.registeredsymbols.Count>0 then //if it has a registered symbol then reinterpret all addresses
|
|
TAddresslist(fOwner).ReinterpretAddresses;
|
|
end;
|
|
except
|
|
//running the script failed, state unchanged
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end
|
|
else
|
|
begin
|
|
//freeze/unfreeze
|
|
|
|
|
|
if state then
|
|
begin
|
|
|
|
f:=GetValue;
|
|
|
|
|
|
try
|
|
|
|
SetValue(f);
|
|
OutputDebugString('SetValue returned');
|
|
|
|
except
|
|
|
|
fActive:=false;
|
|
beep;
|
|
exit;
|
|
end;
|
|
|
|
//still here so F is ok
|
|
//enabled
|
|
|
|
FrozenValue:=f;
|
|
end;
|
|
|
|
fActive:=state;
|
|
end;
|
|
|
|
end else fActive:=state;
|
|
|
|
|
|
if state=false then
|
|
begin
|
|
//on disable or failure setting the state to true, also reset the option if it's allowed to increase/decrease
|
|
allowDecrease:=false;
|
|
allowIncrease:=false;
|
|
end;
|
|
|
|
{$IFNDEF UNIX}
|
|
treenode.update;
|
|
if active and (moActivateChildrenAsWell in options) then
|
|
begin
|
|
//apply this state to all the children
|
|
for i:=0 to treenode.Count-1 do
|
|
TMemoryRecord(treenode[i].data).setActive(true);
|
|
end;
|
|
|
|
if (not active) and (moDeactivateChildrenAsWell in options) then
|
|
begin
|
|
//apply this state to all the children
|
|
for i:=0 to treenode.Count-1 do
|
|
TMemoryRecord(treenode[i].data).setActive(false);
|
|
end;
|
|
{$ENDIF}
|
|
|
|
{ deprecated
|
|
|
|
//6.0 compatibility
|
|
if state then
|
|
LUA_memrec_callback(self, '_memrec_'+description+'_activated')
|
|
else
|
|
LUA_memrec_callback(self, '_memrec_'+description+'_deactivated');
|
|
}
|
|
|
|
//6.5+
|
|
LUA_functioncall('onMemRecPostExecute',[self, state, fActive=state]);
|
|
|
|
//6.1+
|
|
if state and assigned(fonactivate) then fonactivate(self, false, factive); //activated , after
|
|
if not state and assigned(fondeactivate) then fondeactivate(self, false, factive); //deactivated , after
|
|
|
|
SetVisibleChildrenState;
|
|
|
|
end;
|
|
|
|
procedure TMemoryRecord.setVisible(state: boolean);
|
|
begin
|
|
fVisible:=state;
|
|
{$IFNDEF UNIX}
|
|
if treenode<>nil then
|
|
treenode.update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
procedure TMemoryRecord.setShowAsHex(state:boolean);
|
|
var x: QWord;
|
|
begin
|
|
if Active and (fvartype in [vtbyte..vtDouble]) then //currently frozen
|
|
begin
|
|
|
|
if state<>fShowAsHex then //change in state
|
|
begin
|
|
try
|
|
//convert from hex to dec or dec to hex
|
|
if fShowAsHex then
|
|
begin
|
|
//hex->dec
|
|
x:=StrToQWordEx('$'+FrozenValue);
|
|
FrozenValue:=IntToStr(x);
|
|
end
|
|
else
|
|
begin
|
|
//dec->hex
|
|
x:=StrToQWordEx(FrozenValue);
|
|
FrozenValue:=IntToHex(x,1);
|
|
end;
|
|
|
|
except
|
|
exit; //it's not possible to set the state
|
|
end;
|
|
end;
|
|
|
|
end;
|
|
|
|
fShowAsHex:=state;
|
|
{$IFNDEF UNIX}
|
|
if treenode<>nil then
|
|
treenode.Update;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
function TMemoryRecord.getByteSize: integer;
|
|
begin
|
|
result:=0;
|
|
case VarType of
|
|
vtByte: result:=1;
|
|
vtWord: result:=2;
|
|
vtDWord: result:=4;
|
|
vtSingle: result:=4;
|
|
vtDouble: result:=8;
|
|
vtQword: result:=8;
|
|
vtString:
|
|
begin
|
|
result:=Extra.stringData.length;
|
|
if extra.stringData.unicode then result:=result*2;
|
|
end;
|
|
|
|
vtByteArray: result:=extra.byteData.bytelength;
|
|
vtBinary: result:=1+(extra.bitData.Bit+extra.bitData.bitlength div 8);
|
|
vtCustom:
|
|
begin
|
|
if customtype<>nil then
|
|
result:=customtype.bytesize;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemoryRecord.RefreshCustomType;
|
|
begin
|
|
if vartype=vtCustom then
|
|
CustomType:=GetCustomTypeFromName(fCustomTypeName);
|
|
end;
|
|
|
|
function TMemoryRecord.ReinterpretAddress(forceremovalofoldaddress: boolean=false): boolean;
|
|
//Returns false if interpretation failed (not really used for anything right now)
|
|
var
|
|
a: ptrUint;
|
|
s: string;
|
|
i: integer;
|
|
begin
|
|
if forceremovalofoldaddress then
|
|
begin
|
|
RealAddress:=0;
|
|
baseaddress:=0;
|
|
end;
|
|
|
|
a:=symhandler.getAddressFromName(interpretableaddress,false,couldnotinterpretaddress);
|
|
result:=not couldnotinterpretaddress;
|
|
|
|
if result then
|
|
begin
|
|
|
|
s:=trim(interpretableaddress);
|
|
fIsOffset:=(s<>'') and (s[1] in ['+','-']);
|
|
baseaddress:=a;
|
|
end;
|
|
|
|
GetRealAddress;
|
|
|
|
//update the children
|
|
for i:=0 to count-1 do
|
|
Child[i].ReinterpretAddress(forceremovalofoldaddress);
|
|
end;
|
|
|
|
procedure TMemoryRecord.ApplyFreeze;
|
|
var oldvalue, newvalue: string;
|
|
olddecimalvalue, newdecimalvalue: qword;
|
|
oldfloatvalue, newfloatvalue: double;
|
|
begin
|
|
if (not fisgroupheader) and active and (VarType<>vtAutoAssembler) then
|
|
begin
|
|
try
|
|
|
|
if allowIncrease or allowDecrease then
|
|
begin
|
|
//get the new value
|
|
oldvalue:=frozenValue;
|
|
newvalue:=GetValue;
|
|
if showashex or (VarType in [vtByte..vtQword, vtCustom]) then
|
|
begin
|
|
//handle as a decimal
|
|
|
|
|
|
if showAsHex then
|
|
begin
|
|
newdecimalvalue:=StrToInt('$'+newvalue);
|
|
olddecimalvalue:=StrToInt('$'+oldvalue);
|
|
end
|
|
else
|
|
begin
|
|
newdecimalvalue:=StrToInt(newvalue);
|
|
olddecimalvalue:=StrToInt(oldvalue);
|
|
end;
|
|
|
|
if (allowIncrease and (newdecimalvalue>olddecimalvalue)) or
|
|
(allowDecrease and (newdecimalvalue<olddecimalvalue))
|
|
then
|
|
frozenvalue:=newvalue;
|
|
|
|
end
|
|
else
|
|
if Vartype in [vtSingle, vtdouble] then
|
|
begin
|
|
//handle as floating point value
|
|
oldfloatvalue:=strtofloat(oldvalue);
|
|
newfloatvalue:=strtofloat(newvalue);
|
|
|
|
if (allowIncrease and (newfloatvalue>oldfloatvalue)) or
|
|
(allowDecrease and (newfloatvalue<oldfloatvalue))
|
|
then
|
|
frozenvalue:=newvalue;
|
|
|
|
end;
|
|
|
|
try
|
|
setValue(frozenValue, true);
|
|
except
|
|
//new value gives an error, use the old one
|
|
frozenvalue:=oldvalue;
|
|
end;
|
|
end
|
|
else
|
|
setValue(frozenValue, true);
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TMemoryRecord.getAddressString: string;
|
|
begin
|
|
GetRealAddress;
|
|
|
|
if isPointer then
|
|
begin
|
|
if UnreadablePointer then
|
|
result:=rsPqqqqqqqq
|
|
else
|
|
result:=rsP+inttohex(realaddress,8);
|
|
end else
|
|
begin
|
|
if (realaddress=0) and (couldnotinterpretaddress) then
|
|
result:='('+interpretableaddress+')'
|
|
else
|
|
result:=inttohex(realaddress,8);
|
|
end;
|
|
end;
|
|
|
|
function TMemoryRecord.BinaryToString(b: pbytearray; bufsize: integer): string;
|
|
{separate function for the binary value since it's a bit more complex}
|
|
var
|
|
temp,mask: qword;
|
|
begin
|
|
temp:=0; //initialize
|
|
|
|
if bufsize>8 then bufsize:=8;
|
|
|
|
CopyMemory(@temp,b,bufsize);
|
|
|
|
temp:=temp shr extra.bitData.Bit; //shift to the proper start
|
|
mask:=qword($ffffffffffffffff) shl extra.bitData.bitlength; //create a mask that stripps of the excessive bits
|
|
|
|
temp:=temp and (not mask); //temp now only contains the bits that are of meaning
|
|
|
|
|
|
if not extra.bitData.showasbinary then
|
|
result:=inttostr(temp)
|
|
else
|
|
result:=IntToBin(temp);
|
|
end;
|
|
|
|
function TMemoryRecord.GetDisplayValue: string;
|
|
var
|
|
i: integer;
|
|
c: integer;
|
|
begin
|
|
result:=getValue;
|
|
c:=DropDowncount;
|
|
|
|
if fDisplayAsDropDownListItem and (c>0) then
|
|
begin
|
|
//convert the value to a dropdown list item value
|
|
for i:=0 to c-1 do
|
|
begin
|
|
if uppercase(utf8toansi(DropDownValue[i]))=uppercase(result) then
|
|
begin
|
|
if fDropDownDescriptionOnly then
|
|
result:=utf8toansi(DropDownDescription[i])
|
|
else
|
|
result:=result+' : '+utf8toansi(DropDownDescription[i]);
|
|
end;
|
|
|
|
//still here. The value couldn't be found in the list , so just display the value
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
function TMemoryRecord.GetValue: string;
|
|
var
|
|
br: PtrUInt;
|
|
bufsize: integer;
|
|
buf: pointer;
|
|
pb: pbyte absolute buf;
|
|
pba: pbytearray absolute buf;
|
|
pw: pword absolute buf;
|
|
pdw: pdword absolute buf;
|
|
ps: psingle absolute buf;
|
|
pd: pdouble absolute buf;
|
|
pqw: PQWord absolute buf;
|
|
|
|
wc: PWideChar absolute buf;
|
|
c: PChar absolute buf;
|
|
|
|
i: integer;
|
|
e: boolean;
|
|
begin
|
|
|
|
|
|
|
|
result:='';
|
|
if fisGroupHeader then exit;
|
|
|
|
bufsize:=getbytesize;
|
|
if bufsize=0 then exit;
|
|
|
|
if vartype=vtString then
|
|
begin
|
|
inc(bufsize);
|
|
if Extra.stringData.unicode then
|
|
inc(bufsize);
|
|
end;
|
|
|
|
getmem(buf,bufsize);
|
|
|
|
|
|
|
|
GetRealAddress;
|
|
|
|
if ReadProcessMemory(processhandle, pointer(realAddress), buf, bufsize,br) then
|
|
begin
|
|
fIsReadableAddress:=true;
|
|
|
|
case vartype of
|
|
vtCustom:
|
|
begin
|
|
if customtype<>nil then
|
|
begin
|
|
if customtype.scriptUsesFloat then
|
|
result:=FloatToStr(customtype.ConvertDataToFloat(buf, RealAddress))
|
|
else
|
|
if showashex then result:=inttohex(customtype.ConvertDataToInteger(buf, RealAddress),8) else if showassigned then result:=inttostr(integer(customtype.ConvertDataToInteger(buf, RealAddress))) else result:=inttostr(customtype.ConvertDataToInteger(buf, RealAddress));
|
|
end
|
|
else
|
|
result:=rsError;
|
|
end;
|
|
|
|
vtByte : if showashex then result:=inttohex(pb^,2) else if showassigned then result:=inttostr(shortint(pb^)) else result:=inttostr(pb^);
|
|
vtWord : if showashex then result:=inttohex(pw^,4) else if showassigned then result:=inttostr(SmallInt(pw^)) else result:=inttostr(pw^);
|
|
vtDWord: if showashex then result:=inttohex(pdw^,8) else if showassigned then result:=inttostr(Integer(pdw^)) else result:=inttostr(pdw^);
|
|
vtQWord: if showashex then result:=inttohex(pqw^,16) else if showassigned then result:=inttostr(Int64(pqw^)) else result:=inttostr(pqw^);
|
|
vtSingle: if showashex then result:=inttohex(pdw^,8) else result:=FloatToStr(ps^);
|
|
vtDouble: if showashex then result:=inttohex(pqw^,16) else result:=FloatToStr(pd^);
|
|
vtBinary: result:=BinaryToString(buf,bufsize);
|
|
|
|
vtString:
|
|
begin
|
|
pba[bufsize-1]:=0;
|
|
if Extra.stringData.unicode then
|
|
begin
|
|
pba[bufsize-2]:=0;
|
|
result:=WinCPToUTF8(wc);
|
|
end
|
|
else
|
|
result:=WinCPToUTF8(c);
|
|
end;
|
|
|
|
vtByteArray:
|
|
begin
|
|
for i:=0 to bufsize-1 do
|
|
if showashex then
|
|
result:=result+inttohex(pba[i],2)+' '
|
|
else
|
|
result:=result+inttostr(pba[i])+' ';
|
|
|
|
if result<>'' then
|
|
result:=copy(result,1,length(result)-1); //cut off the last space
|
|
end;
|
|
|
|
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
result:='??';
|
|
fIsReadableAddress:=false;
|
|
|
|
if (baseaddress<>0) then
|
|
begin
|
|
baseaddress:=symhandler.getAddressFromName(interpretableaddress,false, e);
|
|
if e then //symbol is gone
|
|
BaseAddress:=0;
|
|
end;
|
|
end;
|
|
|
|
freemem(buf);
|
|
end;
|
|
|
|
function TMemoryrecord.canUndo: boolean;
|
|
begin
|
|
result:=undovalue<>'';
|
|
end;
|
|
|
|
procedure TMemoryRecord.UndoSetValue;
|
|
begin
|
|
if canUndo then
|
|
begin
|
|
try
|
|
setvalue(UndoValue, false);
|
|
except
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
procedure TMemoryRecord.SetValue(v: string);
|
|
begin
|
|
SetValue(v,false);
|
|
end;
|
|
|
|
procedure TMemoryRecord.SetValue(v: string; isFreezer: boolean);
|
|
{
|
|
Changes this address to the value V
|
|
}
|
|
var
|
|
buf: pointer;
|
|
bufsize: integer;
|
|
x: PtrUInt;
|
|
i: integer;
|
|
pb: pbyte absolute buf;
|
|
pba: pbytearray absolute buf;
|
|
pw: pword absolute buf;
|
|
pdw: pdword absolute buf;
|
|
ps: psingle absolute buf;
|
|
pd: pdouble absolute buf;
|
|
pqw: PQWord absolute buf;
|
|
|
|
li: PLongInt absolute buf;
|
|
li64: PQWord absolute buf;
|
|
|
|
wc: PWideChar absolute buf;
|
|
c: PChar absolute buf;
|
|
originalprotection: dword;
|
|
|
|
bts: TBytes;
|
|
mask: qword;
|
|
temp: qword;
|
|
temps: string;
|
|
|
|
tempsw: widestring;
|
|
tempsa: ansistring;
|
|
|
|
mr: TMemoryRecord;
|
|
|
|
unparsedvalue: string;
|
|
check: boolean;
|
|
fs: TFormatSettings;
|
|
|
|
oldluatop: integer;
|
|
begin
|
|
//check if it is a '(description)' notation
|
|
|
|
unparsedvalue:=v;
|
|
|
|
if vartype<>vtString then
|
|
begin
|
|
v:=trim(v);
|
|
|
|
{$IFNDEF UNIX}
|
|
if (length(v)>2) and (v[1]='(') and (v[length(v)]=')') then
|
|
begin
|
|
//yes, it's a (description)
|
|
temps:=copy(v, 2,length(v)-2);
|
|
//search the addresslist for a entry with name (temps)
|
|
|
|
mr:=TAddresslist(fOwner).getRecordWithDescription(temps);
|
|
if mr<>nil then
|
|
v:=mr.GetValue;
|
|
|
|
end;
|
|
{$ENDIF}
|
|
end;
|
|
|
|
if (not isfreezer) then
|
|
undovalue:=GetValue;
|
|
|
|
|
|
{$IFNDEF UNIX}
|
|
if (not isfreezer) and (moRecursiveSetValue in options) then //do this for all it's children
|
|
begin
|
|
for i:=0 to treenode.Count-1 do
|
|
begin
|
|
try
|
|
TMemoryRecord(treenode[i].data).SetValue(v);
|
|
except
|
|
//some won't take the value, like 12.1112 on a 4 byte value, so just skip that error
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
//and now set it for myself
|
|
|
|
|
|
realAddress:=GetRealAddress; //quick update
|
|
|
|
|
|
currentValue:={utf8toansi}(v);
|
|
|
|
if fShowAsHex and (not (vartype in [vtSingle, vtDouble, vtByteArray, vtString] )) then
|
|
begin
|
|
currentvalue:=trim(currentValue);
|
|
if length(currentvalue)>0 then
|
|
begin
|
|
if currentvalue[1]='-' then
|
|
begin
|
|
currentvalue:='-$'+copy(currentvalue,2,length(currentvalue));
|
|
end
|
|
else
|
|
currentvalue:='$'+currentvalue;
|
|
end;
|
|
end;
|
|
|
|
bufsize:=getbytesize;
|
|
|
|
if (vartype=vtbinary) and (bufsize=3) then bufsize:=4;
|
|
if (vartype=vtbinary) and (bufsize>4) then bufsize:=8;
|
|
|
|
getmem(buf,bufsize);
|
|
|
|
|
|
|
|
|
|
VirtualProtectEx(processhandle, pointer(realAddress), bufsize, PAGE_EXECUTE_READWRITE, originalprotection);
|
|
try
|
|
|
|
|
|
check:=ReadProcessMemory(processhandle, pointer(realAddress), buf, bufsize,x);
|
|
if vartype in [vtBinary, vtByteArray] then //fill the buffer with the original byte
|
|
if not check then exit;
|
|
|
|
{$IFNDEF UNIX}
|
|
if (Vartype in [vtByte..vtDouble, vtCustom]) then
|
|
begin
|
|
//check if it's a bracket enclosed value [ ]
|
|
CurrentValue:=trim(CurrentValue);
|
|
if (length(CurrentValue)>2) and (CurrentValue[1]='[') and (currentValue[length(CurrentValue)]=']') then
|
|
begin
|
|
LuaCS.enter;
|
|
try
|
|
oldluatop:=lua_gettop(luavm);
|
|
if lua_dostring(luavm, pchar('return '+copy(CurrentValue,2, length(CurrentValue)-2)))=0 then
|
|
currentValue:=lua_tostring(luavm, -1);
|
|
|
|
lua_settop(luavm, oldluatop);
|
|
finally
|
|
luacs.Leave;
|
|
end;
|
|
end;
|
|
end;
|
|
{$ENDIF}
|
|
|
|
case VarType of
|
|
vtCustom:
|
|
begin
|
|
if customtype<>nil then
|
|
Begin
|
|
if customtype.scriptUsesFloat then
|
|
customtype.ConvertFloatToData(strtofloat(currentValue), ps, RealAddress)
|
|
else
|
|
customtype.ConvertIntegerToData(strtoint(currentValue), pdw, RealAddress);
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
vtByte: pb^:=StrToQWordEx(currentValue);
|
|
vtWord: pw^:=StrToQWordEx(currentValue);
|
|
vtDword: pdw^:=StrToQWordEx(currentValue);
|
|
vtQword: pqw^:=StrToQWordEx(currentValue);
|
|
vtSingle: if (not fShowAsHex) or (not TryStrToInt('$'+currentvalue, li^)) then
|
|
begin
|
|
try
|
|
fs:=DefaultFormatSettings;
|
|
ps^:=StrToFloat(currentValue, fs);
|
|
except
|
|
if fs.DecimalSeparator='.' then
|
|
fs.DecimalSeparator:=','
|
|
else
|
|
fs.DecimalSeparator:='.';
|
|
|
|
ps^:=StrToFloat(currentValue, fs);
|
|
end;
|
|
end;
|
|
|
|
vtDouble: if (not fShowAsHex) or (not TryStrToQWord('$'+currentvalue, li64^)) then
|
|
begin
|
|
try
|
|
fs:=DefaultFormatSettings;
|
|
pd^:=StrToFloat(currentValue, fs);
|
|
except
|
|
if fs.DecimalSeparator='.' then
|
|
fs.DecimalSeparator:=','
|
|
else
|
|
fs.DecimalSeparator:='.';
|
|
|
|
pd^:=StrToFloat(currentValue, fs);
|
|
end;
|
|
end;
|
|
|
|
vtBinary:
|
|
begin
|
|
if not Extra.bitData.showasbinary then
|
|
temps:=currentValue
|
|
else
|
|
temps:=IntToStr(BinToInt(currentValue));
|
|
|
|
temp:=StrToQWordEx(temps);
|
|
temp:=temp shl extra.bitData.Bit;
|
|
mask:=qword($ffffffffffffffff) shl extra.bitData.BitLength;
|
|
mask:=not mask; //mask now contains the length of the bits (4 bits would be 0001111)
|
|
|
|
|
|
mask:=mask shl extra.bitData.Bit; //shift the mask to the proper start position
|
|
temp:=temp and mask; //cut off extra bits
|
|
|
|
case bufsize of
|
|
1: pb^:=(pb^ and (not mask)) or temp;
|
|
2: pw^:=(pw^ and (not mask)) or temp;
|
|
4: pdw^:=(pdw^ and (not mask)) or temp;
|
|
8: pqw^:=(pqw^ and (not mask)) or temp;
|
|
end;
|
|
end;
|
|
|
|
vtString:
|
|
begin
|
|
//x contains the max length in characters for the string
|
|
if extra.stringData.length<length(currentValue) then
|
|
begin
|
|
extra.stringData.length:=length(currentValue);
|
|
freemem(buf);
|
|
bufsize:=getbytesize;
|
|
getmem(buf, bufsize);
|
|
end;
|
|
|
|
x:=bufsize;
|
|
if extra.stringData.unicode then
|
|
x:=bufsize div 2; //each character is 2 bytes so only half the size is available
|
|
|
|
if Extra.stringData.ZeroTerminate then
|
|
x:=min(length(currentValue)+1,x) //also copy the zero terminator
|
|
else
|
|
x:=min(length(currentValue),x);
|
|
|
|
|
|
tempsw:=currentvalue;
|
|
tempsa:=currentvalue;
|
|
|
|
//copy the string to the buffer
|
|
for i:=0 to x-1 do
|
|
begin
|
|
if extra.stringData.unicode then
|
|
begin
|
|
wc[i]:=pwidechar(tempsw)[i];
|
|
end
|
|
else
|
|
begin
|
|
c[i]:=pchar(tempsa)[i];
|
|
end;
|
|
end;
|
|
|
|
if extra.stringData.unicode then
|
|
bufsize:=x*2 //two times the number of characters
|
|
else
|
|
bufsize:=x;
|
|
end;
|
|
|
|
vtByteArray:
|
|
begin
|
|
ConvertStringToBytes(currentValue, showAsHex, bts, true);
|
|
if length(bts)>bufsize then
|
|
begin
|
|
//the user wants to input more bytes than it should have
|
|
Extra.byteData.bytelength:=length(bts); //so next time this won't happen again
|
|
bufsize:=length(bts);
|
|
freemem(buf);
|
|
getmem(buf,bufsize);
|
|
if not ReadProcessMemory(processhandle, pointer(realAddress), buf, bufsize,x) then exit;
|
|
end;
|
|
|
|
bufsize:=min(length(bts),bufsize);
|
|
for i:=0 to bufsize-1 do
|
|
if bts[i]>=0 then
|
|
pba[i]:=bts[i]
|
|
else
|
|
begin
|
|
if bts[i]=-1 then continue;
|
|
|
|
if not showashex then raise exception.create(rsMRNibbleSupportIsOnlyForHexadecimalDisplay);
|
|
|
|
//nibble
|
|
pba[i]:=(((not (bts[i] shr 8)) and $ff) and pba[i]) or (bts[i] and $ff);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
WriteProcessMemory(processhandle, pointer(realAddress), buf, bufsize, x);
|
|
|
|
|
|
finally
|
|
VirtualProtectEx(processhandle, pointer(realAddress), bufsize, originalprotection, originalprotection);
|
|
|
|
end;
|
|
|
|
freemem(buf);
|
|
|
|
frozenValue:=unparsedvalue; //we got till the end, so update the frozen value
|
|
|
|
end;
|
|
|
|
function TMemoryRecord.getBaseAddress: ptrUint;
|
|
begin
|
|
if fIsOffset and hasParent then
|
|
result:=parent.RealAddress+baseaddress //assuming that the parent has had it's real address calculated first
|
|
else
|
|
result:=BaseAddress;
|
|
end;
|
|
|
|
function TMemoryRecord.GetRealAddress: PtrUInt;
|
|
var
|
|
check: boolean;
|
|
realaddress, realaddress2: PtrUInt;
|
|
i: integer;
|
|
count: dword;
|
|
list: array of integer;
|
|
begin
|
|
realAddress:=0;
|
|
realAddress2:=0;
|
|
|
|
if isPointer then //it's a pointer
|
|
begin
|
|
setlength(list, offsetCount);
|
|
for i:=0 to offsetCount-1 do
|
|
list[i]:=offsets[i].offset;
|
|
|
|
//find the address this pointer points to
|
|
result:=getPointerAddress(getBaseAddress, list, UnreadablePointer);
|
|
if UnreadablePointer then
|
|
begin
|
|
realAddress:=0;
|
|
result:=0;
|
|
end;
|
|
end
|
|
else
|
|
result:=getBaseAddress; //not a pointer
|
|
|
|
self.RealAddress:=result;
|
|
end;
|
|
|
|
|
|
|
|
function MemRecHotkeyActionToText(action: TMemrecHotkeyAction): string;
|
|
begin
|
|
//type TMemrecHotkeyAction=(mrhToggleActivation, mrhToggleActivationAllowIncrease, mrhToggleActivationAllowDecrease, mrhSetValue,
|
|
//mrhIncreaseValue, mrhDecreaseValue);
|
|
case action of
|
|
mrhToggleActivation: result:='Toggle Activation';
|
|
mrhToggleActivationAllowIncrease: result:='Toggle Activation Allow Increase';
|
|
mrhToggleActivationAllowDecrease: result:='Toggle Activation Allow Decrease';
|
|
mrhActivate: result:='Activate';
|
|
mrhDeactivate: result:='Deactivate';
|
|
mrhSetValue: result:='Set Value';
|
|
mrhIncreaseValue: result:='Increase Value';
|
|
mrhDecreaseValue: result:='Decrease Value';
|
|
end;
|
|
end;
|
|
|
|
function TextToMemRecHotkeyAction(text: string): TMemrecHotkeyAction;
|
|
begin
|
|
if text = 'Toggle Activation' then result:=mrhToggleActivation else
|
|
if text = 'Toggle Activation Allow Increase' then result:=mrhToggleActivationAllowIncrease else
|
|
if text = 'Toggle Activation Allow Decrease' then result:=mrhToggleActivationAllowDecrease else
|
|
if text = 'Activate' then result:=mrhActivate else
|
|
if text = 'Deactivate' then result:=mrhDeactivate else
|
|
if text = 'Set Value' then result:=mrhSetValue else
|
|
if text = 'Increase Value' then result:=mrhIncreaseValue else
|
|
if text = 'Decrease Value' then result:=mrhDecreaseValue
|
|
else
|
|
result:=mrhToggleActivation;
|
|
end;
|
|
|
|
end.
|
|
|