cheat-engine/Cheat Engine/MemoryRecordUnit.pas

3782 lines
104 KiB
ObjectPascal
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

unit MemoryRecordUnit;
{$mode DELPHI}
interface
{$ifdef windows}
uses
jwawindows, Windows, forms, graphics, Classes, SysUtils, controls, stdctrls, comctrls,
symbolhandler, SymbolListHandler, cefuncproc,newkernelhandler, hotkeyhandler,
dom, XMLRead,XMLWrite, customtypehandler, fileutil, LCLProc, commonTypeDefs,
pointerparser, LazUTF8, LuaClass, math, betterControls, memrecDataStructures;
{$endif}
{$ifdef darwin}
uses
macport, forms, graphics, Classes, SysUtils, controls, stdctrls, comctrls,
symbolhandler, SymbolListHandler, cefuncproc,newkernelhandler, hotkeyhandler,
dom, XMLRead,XMLWrite, CustomTypeHandler, fileutil, LCLProc, commonTypeDefs,
pointerparser, LazUTF8, LuaClass, math,memrecDataStructures;
{$endif}
{$ifdef jni}
//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';
rsToggleActivation = 'Toggle Activation';
rsToggleActivationAllowIncrease = 'Toggle Activation Allow Increase';
rsToggleActivationAllowDecrease = 'Toggle Activation Allow Decrease';
rsActivate = 'Activate';
rsDeactivate = 'Deactivate';
rsSetValue = 'Set Value';
rsIncreaseValue = 'Increase Value';
rsDecreaseValue = 'Decrease Value';
rsAdjustMRwithRelativeAddress = 'Do you wish to adjust memory records with relative addresses as well?';
type
TMemrecHotkeyAction=(mrhToggleActivation=0, mrhToggleActivationAllowIncrease=1, mrhToggleActivationAllowDecrease=2, mrhActivate=3, mrhDeactivate=4, mrhSetValue=5, mrhIncreaseValue=6, mrhDecreaseValue=7);
TFreezeType=(ftFrozen, ftAllowIncrease, ftAllowDecrease);
TMemrecOption=(moHideChildren, moActivateChildrenAsWell, moDeactivateChildrenAsWell, moRecursiveSetValue, moAllowManualCollapseAndExpand, moManualExpandCollapse, moAlwaysHideChildren);
TMemrecOptions=set of TMemrecOption;
TMemoryRecordHotkey=class;
TMemoryRecord=class;
TMemoryRecordProcessingThread=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)
finvalid: boolean;
funparsed: boolean;
hasValue: boolean;
fOnlyUpdateAfterInterval: boolean;
fOnlyUpdateWithReinterpret: boolean;
fUpdateInterval: dword;
lastOffsetBase: ptruint;
LastUpdateInterval: qword;
forced: boolean; //set to true if you wish to let getOffset ignore the no-update rules
function getOffsetNoBase: integer;
procedure cleanupluaref;
public
procedure forceUpdate;
function getOffset(currentBase: ptruint): integer;
procedure setOffset(o: integer);
procedure setOffsetText(s: string);
constructor create(owner: TMemoryRecord);
destructor destroy; override;
published
property offset: integer read getOffsetNoBase write setOffset;
property offsetText: string read text write setOffsetText;
property unparsed: boolean read funparsed;
property invalid: boolean read fInvalid;
property OnlyUpdateWithReinterpret: boolean read fOnlyUpdateWithReinterpret write fOnlyUpdateWithReinterpret;
property OnlyUpdateAfterInterval: boolean read fOnlyUpdateAfterInterval write fOnlyUpdateAfterInterval;
property UpdateInterval: DWORD read fUpdateInterval write fUpdateInterval;
end;
TMemrecOffsetList=array of TMemrecOffset;
TMemoryRecordActivateEvent=function (sender: TObject; before, currentstate: boolean): boolean of object;
TGetDisplayValueEvent=function(sender: TObject; var value: string): boolean of object;
TMemoryRecordChangedValueEvent=procedure (sender: TObject; oldvalue: string; newvalue: string) of object;
TMemoryRecord=class
private
fID: integer;
FrozenValue : string;
CurrentValue : string;
UndoValue : string; //keeps the last value before a manual edit
LastSeenValue : string;
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
fDescription : string;
fOptions: TMemrecOptions;
fCustomType: 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
fisAddressGroupHeader: Boolean; // AddressGroupHeader is a special case of GroupHeader
fIsReadableAddress: boolean;
fDropDownList: Tstringlist;
fDropDownReadOnly: boolean;
fDropDownDescriptionOnly: boolean;
fDisplayAsDropDownListItem: boolean;
fDropDownLinked: boolean;
fDropDownLinkedMemrec: string;
linkedDropDownMemrec: TMemoryRecord;
memrecsLinkedToMe: array of TMemoryRecord; // a list of all memrecs linked to this memrec
fDontSave: boolean;
fAsync: boolean;
processingThread: TMemoryRecordProcessingThread; //not nil when doing work
processingTimeStart: qword;
wantedstate: boolean; //last state the user wanted to set it to
luaref: integer; //luaclass object to this instance
fonactivate, fondeactivate: TMemoryRecordActivateEvent;
fOnDestroy: TNotifyEvent;
fOnGetDisplayValue: TGetDisplayValueEvent;
fOnValueChanged, fOnValueChangedByUser: TMemoryRecordChangedValueEvent;
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;
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);
procedure setAddressGroupHeader(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 getDropDownReadOnly: boolean;
function getDropDownDescriptionOnly: boolean;
function getDisplayAsDropDownListItem: boolean;
function hasMouseOver: boolean;
procedure setDropDownLinkedMemrec(s: string);
function GetCollapsed: boolean;
procedure SetCollapsed(state: boolean);
procedure processingDone; //called by the processingThread when finished
procedure setDescription(d: string);
public
interpretableaddress: string;
Extra: TMemRecExtraData;
AutoAssemblerData: TMemRecAutoAssemblerData;
{$ifndef jni}
treenode: TTreenode;
autoAssembleWindow: TCustomForm; //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;
fScriptHotKey: TMemoryRecordHotkey; //set when a hotkey is used to toggle a script
function getuniquehotkeyid: integer;
//free for editing by user:
function hasSelectedParent: boolean;
function hasParent: boolean;
procedure appendToEntry(memrec: TMemoryrecord);
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;
function parseAddressString(s: string; out address: ptruint): 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; onlyWhileDown: boolean=false): TMemoryRecordHotkey;
function removeHotkey(hk: TMemoryRecordHotkey): boolean;
procedure DoHotkey(hk :TMemoryRecordHotkey); //execute the specific hotkey action
procedure DoHotkeyDisable(hk :TMemoryRecordHotkey); //disable the specific hotkey
procedure disablewithoutexecute;
procedure refresh;
procedure getXMLNode(node: TDOMNode; selectedOnly: boolean);
procedure setXMLnode(CheatEntry: TDOMNode);
function getCurrentDropDownIndex: integer;
procedure SetVisibleChildrenState;
procedure cleanupPointerOffsets;
function getLuaRef: integer;
function isProcessing: boolean;
function getProcessingTime: qword;
function getlinkedDropDownMemrec: TMemoryRecord;
function getlinkedDropDownMemrec_LoopDetected: boolean;
procedure replaceDescription(replace_find, replace_with: string; childrenaswell: boolean);
procedure adjustAddressby(offset, pointerlastoffset: int64; childrenaswell: boolean; relativeaswell: boolean=false);
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;
property offsets[index: integer]: TMemrecOffset read getPointerOffset;
published
property IsGroupHeader: boolean read fisGroupHeader write fisGroupHeader;
property IsAddressGroupHeader: boolean read fisAddressGroupHeader write setAddressGroupHeader;
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 CustomType: TCustomType read fCustomType;
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 DropDownLinked: boolean read fDropDownLinked write fDropDownLinked;
property DropDownLinkedMemrec: string read fDropDownLinkedMemrec write setDropDownLinkedMemrec;
property DropDownList: TStringlist read fDropDownList;
property DropDownReadOnly: boolean read getDropDownReadOnly write fDropDownReadOnly;
property DropDownDescriptionOnly: boolean read getDropDownDescriptionOnly write fDropDownDescriptionOnly;
property DisplayAsDropDownListItem: boolean read getDisplayAsDropDownListItem 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 write appendToEntry;
property OnActivate: TMemoryRecordActivateEvent read fOnActivate write fOnActivate;
property OnDeactivate: TMemoryRecordActivateEvent read fOnDeActivate write fOndeactivate;
property OnDestroy: TNotifyEvent read fOnDestroy write fOnDestroy;
property OnGetDisplayValue: TGetDisplayValueEvent read fOnGetDisplayValue write fOnGetDisplayValue;
property OffsetCount: integer read getoffsetCount write setOffsetCount;
property Async: Boolean read fAsync write fAsync;
property AsyncProcessing: Boolean read isProcessing;
property AsyncProcessingTime: qword read getProcessingTime;
property ScriptHotKey: TMemoryRecordHotkey read fScriptHotKey;
property LastAAExecutionFailed: boolean read AutoAssemblerData.lastExecutionFailed;
property LastAAExecutionFailedReason: string read AutoAssemblerData.lastExecutionFailedReason;
property Description: string read fDescription write setDescription;
property CachedAddress: ptruint read realAddress;
property HasMouseFocus: boolean read hasMouseOver;
property OnValueChanged: TMemoryRecordChangedValueEvent read fOnValueChanged write fOnValuechanged;
property OnValueChangedByUser: TMemoryRecordChangedValueEvent read fOnValueChangedByUser write fOnValueChangedByUser;
end;
THKSoundFlag=(hksPlaySound=0, hksSpeakText=1, hksSpeakTextEnglish=2); //playSound excludes speakText
TMemoryRecordHotkey=class
private
fOnHotkey: TNotifyevent;
fOnPostHotkey: TNotifyevent;
factivateSound: string;
fdeactivateSound: string;
fActivateSoundFlag: THKSoundFlag;
fDeactivateSoundFlag: THKSoundFlag;
fValueAtActivation: string;
public
fID: integer;
fDescription: string;
fOnlyWhileDown: boolean;
fOwner: TMemoryRecord;
keys: Tkeycombo;
fAction: TMemrecHotkeyAction;
fValue: string;
fActive: boolean;
Down: boolean;
procedure playActivateSound;
procedure playDeactivateSound;
procedure doHotkey;
procedure doHotkeyDisable;
procedure registerKeys;
constructor create(AnOwner: TMemoryRecord);
destructor destroy; override;
published
property ActivateSoundFlag: THKSoundFlag read fActivateSoundFlag write fActivateSoundFlag;
property DeactivateSoundFlag: THKSoundFlag read fDeactivateSoundFlag write fDeactivateSoundFlag;
property ActivateSound: string read factivateSound write factivateSound;
property DeactivateSound: string read fdeactivateSound write fdeactivateSound;
property Description: string read fDescription write fDescription;
property Action: TMemrecHotkeyAction read fAction write fAction;
property Value: string read fValue write fValue;
property OnlyWhileDown: boolean read fOnlyWhileDown write fOnlyWhileDown;
property Owner: TMemoryRecord read fOwner;
property ID: integer read fID;
property OnHotkey: TNotifyEvent read fOnHotkey write fOnHotkey;
property OnPostHotkey: TNotifyEvent read fOnPostHotkey write fOnPostHotkey;
property Active: boolean read fActive write fActive;
end;
TMemoryRecordProcessingThread=class(TThread)
private
owner: TMemoryRecord;
state: boolean;
procedure reinterpretAddresses;
public
procedure Execute; override;
constructor Create(o: TMemoryRecord; s: boolean);
end;
function MemRecHotkeyActionToText(action: TMemrecHotkeyAction): string;
function TextToMemRecHotkeyAction(text: string): TMemrecHotkeyAction;
implementation
{$ifdef jni}
uses processhandlerunit, Parsers;
{$else}
uses mainunit, addresslist, formsettingsunit, LuaHandler, lua, lauxlib, lualib, Contnrs,
processhandlerunit, Parsers, {$ifdef windows}winsapi,{$endif}autoassembler, globals{$ifdef windows}, cheatecoins{$endif};
{$endif}
resourcestring
rsUnknown = 'Unknown';
{---------------------TMemoryRecordProcessingThread-------------------------}
procedure TMemoryRecordProcessingThread.reinterpretAddresses;
begin
TAddresslist(owner.fOwner).ReinterpretAddresses;
end;
procedure TMemoryRecordProcessingThread.Execute;
begin
try
if autoassemble(owner.autoassemblerdata.script, false, state, false, false, owner.autoassemblerdata.disableinfo, owner) then
begin
owner.fActive:=state;
if (owner.autoassemblerdata.disableinfo.registeredsymbols.Count>0) or (owner.autoassemblerdata.disableinfo.ccodesymbols.count>0) then //if it has a registered symbol then reinterpret all addresses
Queue(ReinterpretAddresses);
owner.autoassemblerdata.lastExecutionFailed:=false;
end
else
begin
owner.autoassemblerdata.lastExecutionFailed:=true;
owner.autoassemblerdata.lastExecutionFailedReason:='Unknown';
end;
except
//running the script failed, state unchanged
on e:exception do
begin
owner.autoassemblerdata.lastExecutionFailed:=true;
owner.autoassemblerdata.lastExecutionFailedReason:=e.message;
OutputDebugString(e.message);
end;
end;
Queue(owner.processingDone);
end;
constructor TMemoryRecordProcessingThread.Create(o: TMemoryRecord; s: boolean);
begin
owner:=o;
state:=s;
inherited create(false);
end;
{-----------------------------TMemrecOffset---------------------------------}
procedure TMemrecOffset.forceUpdate;
begin
Forced:=true;
end;
function TMemrecOffset.getOffsetNoBase: integer;
begin
result:=getOffset(lastOffsetBase);
end;
function TMemrecOffset.getOffset(currentBase: ptruint): integer;
var
e: boolean;
memrecluaobjectref: integer;
stack: integer;
begin
lastOffsetBase:=currentbase;
if special then
begin
if (not forced) then
begin
if OnlyUpdateWithReinterpret and hasValue then exit(foffset);
if OnlyUpdateAfterInterval and (GetTickCount64<LastUpdateInterval+UpdateInterval) then exit(foffset);
end
else
forced:=false;
LastUpdateInterval:=GetTickCount64;
finvalid:=true;
hasValue:=false;
foffset:=0;
//parse it/call the lua function
if luaref<>-1 then
begin
{$ifndef JNI}
memrecluaobjectref:=fowner.getLuaRef;
stack:=lua_Gettop(luavm);
try
lua_rawgeti(Luavm, LUA_REGISTRYINDEX, LuaRef);
lua_rawgeti(Luavm, LUA_REGISTRYINDEX, memrecluaobjectref);
lua_pushinteger(luavm, currentBase);
if lua_pcall(Luavm, 2, 1,0)=0 then
begin
if lua_isnumber(Luavm, -1) then
begin
foffset:=lua_tointeger(Luavm, -1);
finvalid:=false;
end
else
begin
funparsed:=true;
cleanupluaref;
end;
end
else
begin
funparsed:=true;
cleanupluaref;
end;
lua_pop(luavm, 1);
finally
lua_settop(luavm, stack);
end;
if funparsed then
setoffsetText(text); //will be fixed next run
{$endif}
end;
if luaref=-1 then
begin
foffset:=symhandler.getAddressFromName(text, false, finvalid);
if finvalid then
setoffsetText(text);
end;
if not finvalid then
hasValue:=true;
end
else
finvalid:=false;
result:=foffset;
end;
procedure TMemrecOffset.cleanupluaref;
begin
{$ifndef JNI}
if luaref<>-1 then //dereference this lua function
begin
luaL_unref(LuaVM, LUA_REGISTRYINDEX, luaref);
luaref:=-1;
end;
{$endif}
end;
procedure TMemrecOffset.setOffset(o: integer);
begin
if o<0 then
offsettext:='-'+inttohex(-o,1)
else
offsettext:=inttohex(o,1);
end;
procedure TMemrecOffset.setOffsetText(s: string);
var
e: boolean;
s2: string;
stack: integer;
begin
funparsed:=true;
cleanupluaref;
text:=s;
special:=false;
try
foffset:=StrToQWordEx(ConvertHexStrToRealStr(s));
funparsed:=false;
exit;
except
foffset:=symhandler.getAddressFromNameShallow(s, false, e);
if not e then
begin
special:=true;
funparsed:=false;
exit;
end;
end;
special:=true;
//parse it as lua (because that can be called by ref, which is faster, and gets the memrec and address variables), if that fails, try the symbolhandler
s2:='local memrec, address=... ; return '+s;
stack:=lua_Gettop(luavm);
try
if luaL_loadstring(luavm, pchar(s2))=0 then
if lua_isfunction(luavm,-1) then //store a reference to this function
begin
lua_pushvalue(LuaVM,-1);
lua_rawgeti(Luavm, LUA_REGISTRYINDEX, fowner.getLuaRef);
lua_pushinteger(luavm, lastOffsetBase);
if lua_pcall(Luavm, 2,1,0)=0 then
begin
if lua_isnumber(luavm,-1) then
begin
lua_pop(luavm,1);
if lua_isfunction(luavm,-1) then
luaref:=luaL_ref(luavm, LUA_REGISTRYINDEX)
else
raise exception.create('Invalid lua state');
end;
end;
end;
finally
lua_settop(luavm, stack);
end;
funparsed:=luaref=-1;
if funparsed then
begin
foffset:=symhandler.getAddressFromName(s, false, e);
funparsed:=e;
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;
factive:=true;
end;
destructor TMemoryRecordHotkey.destroy;
begin
UnregisterAddressHotkey(self);
//remove this hotkey from the memoryrecord
if owner<>nil then
begin
owner.hotkeylist.Remove(self);
if owner.fScriptHotKey=self then
owner.fScriptHotKey:=nil;
end;
inherited destroy;
end;
procedure TMemoryRecordHotkey.registerKeys;
begin
UnregisterAddressHotkey(self);
RegisterHotKey2(mainform.handle, -1, keys, self);
end;
procedure TMemoryRecordHotkey.doHotkeyDisable;
begin
if Down then
begin
owner.DoHotkeyDisable(self);
down:=false;
end;
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 OnlyWhileDown then
down:=true;
if assigned(fonPostHotkey) then
fOnPostHotkey(self);
end;
procedure TMemoryRecordHotkey.playActivateSound;
var s: string;
begin
{$ifdef windows}
if activateSound<>'' then
begin
if ActivateSoundFlag in [hksSpeakText, hksSpeakTextEnglish] then
begin
s:=ActivateSound;
s:=StringReplace(s,'{MRDescription}', fowner.Description,[rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{Description}', Description, [rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{MRValue}', fowner.Value,[rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{Value}', Value, [rfIgnoreCase, rfReplaceAll]);
if ActivateSoundFlag=hksSpeakTextEnglish then
speak('<voice required="Language=409">'+s+'</voice>')
else
speak(s)
end
else
LUA_DoScript('playSound(findTableFile([['+activateSound+']]))');
end;
{$endif}
end;
procedure TMemoryRecordHotkey.playDeactivateSound;
var s: string;
begin
{$ifdef windows}
if DeactivateSound<>'' then
begin
if DeactivateSoundFlag in [hksSpeakText, hksSpeakTextEnglish] then
begin
s:=DeactivateSound;
s:=StringReplace(s,'{MRDescription}', fowner.Description,[rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{Description}', Description, [rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{MRValue}', fowner.Value,[rfIgnoreCase, rfReplaceAll]);
s:=StringReplace(s,'{Value}', Value, [rfIgnoreCase, rfReplaceAll]);
if DeactivateSoundFlag=hksSpeakTextEnglish then
speak('<voice required="Language=409">'+s+'</voice>')
else
speak(s);
end
else
LUA_DoScript('playSound(findTableFile([['+deactivateSound+']]))');
end;
{$endif}
end;
{---------------------------------MemoryRecord---------------------------------}
procedure TMemoryRecord.SetAddressGroupHeader(state: boolean);
begin
fisGroupHeader:=true;
fisAddressGroupHeader:=state;
end;
function TMemoryRecord.GetCollapsed: boolean;
begin
{$ifndef jni}
result:=not treenode.Expanded;
{$endif}
end;
procedure TMemoryRecord.SetCollapsed(state: boolean);
begin
{$ifndef jni}
if state then
treenode.Collapse(false)
else
treenode.Expand(false);
{$endif}
end;
procedure TMemoryRecord.setDropDownLinkedMemrec(s: string);
var i: integer;
begin
// changing DropDownLinkedMemrec to other memrec
// remove old link(s) (if any)
if linkedDropDownMemrec<>nil then
for i:=0 to length(linkedDropDownMemrec.memrecsLinkedToMe)-1 do
if linkedDropDownMemrec.memrecsLinkedToMe[i]=self then
linkedDropDownMemrec.memrecsLinkedToMe[i]:=nil;
fDropDownLinkedMemrec:=s;
linkedDropDownMemrec:=nil;
end;
function TMemoryRecord.getDropDownCount: integer;
var mr: tmemoryrecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDropDownCount)
else
exit(0);
end;
result:=fDropDownList.count;
end;
function TMemoryRecord.getDropDownValue(index: integer): string;
var mr: tmemoryrecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDropDownValue(index))
else
exit('');
end;
result:='';
if index<DropDownCount then
result:=copy(fDropDownList[index], 1, pos(':', fDropDownList[index])-1);
end;
function TMemoryRecord.getDropDownDescription(index: integer): string;
var mr: tmemoryrecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDropDownDescription(index))
else
exit('');
end;
result:='';
if index<DropDownCount then
result:=copy(fDropDownList[index], pos(':', fDropDownList[index])+1, length(fDropDownList[index]));
end;
function TMemoryRecord.getCurrentDropDownIndex: integer;
var i: integer;
mr: tmemoryrecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
begin
result:=-1;
for i:=0 to mr.DropDownCount-1 do
begin
if lowercase(Value)=lowercase(mr.DropDownValue[i]) then
result:=i;
end;
exit;
end
else
exit(-1);
end;
result:=-1;
for i:=0 to DropDownCount-1 do
begin
if lowercase(Value)=lowercase(DropDownValue[i]) then
result:=i;
end;
end;
function TMemoryRecord.getDropDownReadOnly: boolean;
var mr: TMemoryRecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDropDownReadOnly)
else
exit(false);
end
else
result:=fDropDownReadOnly;
end;
function TMemoryRecord.getDropDownDescriptionOnly: boolean;
var mr: TMemoryRecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDropDownDescriptionOnly)
else
exit(false);
end
else
result:=fDropDownDescriptionOnly;
end;
function TMemoryRecord.getDisplayAsDropDownListItem: boolean;
var mr: TMemoryRecord;
begin
if DropDownLinked then
begin
mr:=getlinkedDropDownMemrec;
if mr<>nil then
exit(mr.getDisplayAsDropDownListItem)
else
exit(false);
end
else
result:=fDisplayAsDropDownListItem;
end;
function TMemoryRecord.getChildCount: integer;
begin
result:=0;
{$ifndef jni}
if treenode<>nil then
result:=treenode.Count;
{$endif}
end;
function TMemoryRecord.getChild(index: integer): TMemoryRecord;
begin
{$IFNDEF jni}
if index<Count then
result:=TMemoryRecord(treenode.Items[index].Data)
else
{$ENDIF}
result:=nil;
end;
procedure TMemoryRecord.replaceDescription(replace_find, replace_with: string; childrenaswell: boolean);
var i: integer;
begin
if replace_find='' then exit;
Description:=stringreplace(Description,replace_find,replace_with,[rfReplaceAll,rfIgnoreCase]);
if childrenaswell then
begin
for i:=0 to Count-1 do
Child[i].replaceDescription(replace_find, replace_with, childrenaswell);
end;
end;
procedure TMemoryRecord.adjustAddressby(offset, pointerlastoffset: int64; childrenaswell: boolean; relativeaswell: boolean=false);
var
s: string;
x: ptruint;
i: integer;
appendText: boolean = false;
begin
if (offset=0) and (pointerlastoffset=0) then exit;
if (offset<>0) and (interpretableaddress<>'') then
begin
s:=trim(interpretableaddress);
if s<>'' then
begin
if not (s[1] in ['-', '+']) then
begin
try
x:=symhandler.getAddressFromName(interpretableaddress);
x:=x+offset;
interpretableaddress:=symhandler.getNameFromAddress(x,true,true, false);
except
if getBaseAddress<>0 then
interpretableaddress:=inttohex(getBaseAddress+offset,8)
else
appendText:=true;
end;
end
else if relativeaswell then // relative address
begin
try
x:=symhandler.getAddressFromName(interpretableaddress);
x:=x+offset;
interpretableaddress:=IntToHexSignedWithPlus(int64(x),1);
except
appendText:=true;
end;
end;
end;
if appendText then
interpretableaddress:=interpretableaddress+IntToHexSignedWithPlus(offset,1); // append text
ReinterpretAddress;
end;
if (pointerlastoffset<>0) and isPointer then
try
x:=symhandler.getAddressFromName(offsets[0].offsetText);
x:=x+pointerlastoffset;
offsets[0].offsetText:=IntToHexSigned(int64(x),1);
except
offsets[0].offsetText:=offsets[0].offsetText+IntToHexSignedWithPlus(pointerlastoffset,1); // append text
end;
if childrenaswell then
for i:=0 to count-1 do
Child[i].adjustAddressby(offset, pointerlastoffset, childrenaswell, relativeaswell);
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
{$ifndef jni}
if luaref=-1 then
begin
luaclass_newClass(luavm, self);
luaref:=luaL_ref(luavm, LUA_REGISTRYINDEX);
end;
{$endif}
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
taddresslist(fowner).MemrecDescriptionChange(self, fdescription,'');
if processingThread<>nil then
begin
processingThread.Terminate;
processingThread.WaitFor;
freeandnil(processingThread);
end;
{----------------DropDownList linking----------------}
// remove the link(s) (if any)
if linkedDropDownMemrec<>nil then
for i:=0 to length(linkedDropDownMemrec.memrecsLinkedToMe)-1 do
if linkedDropDownMemrec.memrecsLinkedToMe[i]=self then
linkedDropDownMemrec.memrecsLinkedToMe[i]:=nil;
for i:=0 to length(memrecsLinkedToMe)-1 do
if memrecsLinkedToMe[i]<>nil then
memrecsLinkedToMe[i].linkedDropDownMemrec:=nil;
setlength(memrecsLinkedToMe,0);
{-------------^^^DropDownList linking^^^-------------}
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.disableinfo<>nil then
freeandnil(autoassemblerdata.disableinfo);
//free the group's children
{$IFNDEF JNI}
while (treenode.count>0) do
TMemoryRecord(treenode[0].data).free;
if treenode<>nil then
treenode.free;
{$ENDIF}
if fDropDownList<>nil then
freeandnil(fDropDownList);
{$ifndef jni}
if luaref<>-1 then
luaL_unref(LuaVM, LUA_REGISTRYINDEX, luaref);
{$endif}
inherited Destroy;
end;
procedure TMemoryRecord.SetVisibleChildrenState;
{Called when options change and when children are assigned}
begin
{$IFNDEF jni}
if ((not factive) and (moHideChildren in foptions)) or (moAlwaysHideChildren in fOptions) then
treenode.Collapse(true)
else
treenode.Expand(false);
{$ENDIF}
end;
procedure TMemoryRecord.setOptions(newOptions: TMemrecOptions);
var oldoptions: TMemrecOptions;
begin
oldoptions:=foptions;
if (moHideChildren in options) and (moAlwaysHideChildren in newOptions) then //mutually exclusive
newOptions:=newOptions-[moHideChildren];
if (moAlwaysHideChildren in options) and (moHideChildren in newOptions) then
newoptions:=newoptions-[moAlwaysHideChildren];
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.codepage:=false;
extra.stringData.ZeroTerminate:=true;
end;
vtCodePageString:
begin
fvartype:=vtString;
extra.stringData.unicode:=false;
extra.stringData.codepage:=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
if (c=graphics.clWindowText) or
(c=graphics.clDefault)
then //in case clWindowText isn't good to use
c:=clWindowtext;
fColor:=c;
{$IFNDEF jni}
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 jni}
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];
a:=tempnode.Attributes.GetNamedItem('moAlwaysHideChildren');
if (a<>nil) and (a.TextContent='1') then
foptions:=foptions+[moAlwaysHideChildren];
end;
end;
tempnode:=CheatEntry.FindNode('DropDownListLink');
if tempnode<>nil then
begin
fDropDownLinked:=true;
fDropDownLinkedMemrec:=tempnode.TextContent;
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);
if (fcolor=graphics.clWindowText) or
(fcolor=graphics.clDefault)
then
fcolor:=clWindowtext;
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(false);
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('CodePage');
if tempnode<>nil then
extra.stringData.Codepage:=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);
if AutoAssemblerData.disableinfo<>nil then
freeandnil(AutoAssemblerData.disableinfo);
AutoAssemblerData.disableinfo:=TDisableInfo.create;
AutoAssemblerData.script:=tstringlist.Create;
AutoAssemblerData.script.text:=tempnode.TextContent;
a:=tempnode.Attributes.GetNamedItem('Async');
if (a<>nil) then fAsync:=a.TextContent='1';
end;
end;
end;
tempnode:=CheatEntry.FindNode('Address');
if tempnode<>nil then
begin
interpretableaddress:=tempnode.TextContent;
fisAddressGroupHeader:=fisGroupHeader;
end;
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
tempnode2:=tempnode.ChildNodes[i];
if tempnode2.NodeName='Offset' then
begin
fpointeroffsets[j].offsetText:=tempnode2.TextContent;
a:=tempnode2.Attributes.GetNamedItem('Interval');
if (a<>nil) then
begin
try
fpointeroffsets[j].UpdateInterval:=StrToInt(a.TextContent);
fpointeroffsets[j].OnlyUpdateAfterInterval:=true;
except
end;
end;
a:=tempnode2.Attributes.GetNamedItem('UpdateOnFullRefresh');
if (a<>nil) and (a.TextContent='1') then
fpointeroffsets[j].OnlyUpdateWithReinterpret:=true;
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
a:=tempnode.ChildNodes[i].Attributes.GetNamedItem('Active');
if a<>nil then
hk.Active:=a.TextContent<>'0';
a:=tempnode.ChildNodes[i].Attributes.GetNamedItem('OnlyWhileDown');
if (a<>nil) then
hk.OnlyWhileDown:=a.TextContent='1';
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
begin
hk.activateSound:=tempnode2.TextContent;
a:=tempnode2.Attributes.GetNamedItem('TTS');
if (a<>nil) then
begin
if (a.TextContent='EN') then
hk.ActivateSoundFlag:=hksSpeakTextEnglish
else
hk.ActivateSoundFlag:=hksSpeakText;
end
else
hk.ActivateSoundFlag:=hksPlaySound;
end;
tempnode2:=tempnode.childnodes[i].findnode('DeactivateSound');
if tempnode2<>nil then
begin
hk.deactivateSound:=tempnode2.TextContent;
a:=tempnode2.Attributes.GetNamedItem('TTS');
if (a<>nil) then
begin
if (a.TextContent='EN') then
hk.DeactivateSoundFlag:=hksSpeakTextEnglish
else
hk.DeactivateSoundFlag:=hksSpeakText;
end
else
hk.DeactivateSoundFlag:=hksPlaySound;
end;
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;
hk.registerKeys;
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;
procedure TMemoryRecord.appendToEntry(memrec: TMemoryrecord);
begin
treenode.MoveTo(memrec.treenode, naAddChild);
memrec.SetVisibleChildrenState;
end;
function TMemoryRecord.getParent: TMemoryRecord;
{$IFNDEF jni}
var tn: TTreenode;
{$ENDIF}
begin
{$IFNDEF jni}
result:=nil;
tn:=treenode.parent;
if tn<>nil then
result:=TMemoryRecord(tn.data);
{$ENDIF}
end;
function TMemoryRecord.hasParent: boolean;
begin
{$IFNDEF jni}
result:=(treenode<>nil) and (treenode.parent<>nil);
{$ENDIF}
end;
function TMemoryRecord.hasSelectedParent: boolean;
{$IFNDEF jni}
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 jni}
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 jni}
var
doc: TDOMDocument=nil;
cheatEntry: TDOMNode=nil;
cheatEntries: TDOMNode=nil;
offsets: TDOMNode=nil;
hks, hk,hkkc: TDOMNode;
opt: TDOMNode=nil;
laststate: TDOMNode=nil;
soundentry: TDOMNode=nil;
n: TDOMNode=nil;
tn: TTreenode=nil;
i,j: integer;
a:TDOMAttr;
s: ansistring;
ddl: TDOMNode=nil;
offset: TDOMNode=nil;
procedure AddressAndOffsets();
var i: integer;
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
begin
offset:=Offsets.AppendChild(doc.CreateElement('Offset'));
offset.TextContent:=fpointeroffsets[i].offsetText;
if fpointeroffsets[i].OnlyUpdateAfterInterval then
begin
a:=doc.CreateAttribute('Interval');
a.TextContent:=inttostr(fpointeroffsets[i].UpdateInterval);
offset.Attributes.SetNamedItem(a);
end;
if fpointeroffsets[i].OnlyUpdateWithReinterpret then
begin
a:=doc.CreateAttribute('UpdateOnFullRefresh');
a.TextContent:='1';
offset.Attributes.SetNamedItem(a);
end;
end;
cheatEntry.AppendChild(Offsets);
end;
end;
{$ENDIF}
begin
{$IFNDEF JNI}
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;
if moAlwaysHideChildren in options then
begin
a:=doc.CreateAttribute('moAlwaysHideChildren');
a.TextContent:='1';
opt.Attributes.SetNamedItem(a);
end;
end;
if DropDownLinked then
begin
ddl:=cheatEntry.AppendChild(doc.CreateElement('DropDownListLink'));
ddl.TextContent:=DropDownLinkedMemrec;
end
else
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<>'??') and (ssCtrl in GetKeyShiftState) 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;
if (laststate<>nil) and Active then
begin
a:=doc.CreateAttribute('Activated');
a.TextContent:='1';
laststate.Attributes.SetNamedItem(a);
end;
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) and
(fcolor<>graphics.clDefault)
then
cheatEntry.AppendChild(doc.CreateElement('Color')).TextContent:=inttohex(fcolor,6);
if fisGroupHeader then
begin
cheatEntry.AppendChild(doc.CreateElement('GroupHeader')).TextContent:='1';
if fisAddressGroupHeader then AddressAndOffsets;
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('CodePage')).TextContent:=BoolToStr(extra.stringData.codepage,'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
n:=cheatEntry.AppendChild(doc.CreateElement('AssemblerScript'));
if fAsync then
begin
a:=doc.CreateAttribute('Async');
a.TextContent:='1';
n.Attributes.SetNamedItem(a);
end;
n.TextContent:=AutoAssemblerData.script.Text;
end;
end;
if VarType<>vtAutoAssembler then AddressAndOffsets;
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);
if hotkey[i].Active=false then
begin
a:=doc.CreateAttribute('Active');
a.TextContent:='0';
hk.Attributes.SetNamedItem(a);
end;
if hotkey[i].OnlyWhileDown then
begin
a:=doc.CreateAttribute('OnlyWhileDown');
a.TextContent:='1';
hk.Attributes.SetNamedItem(a);
end;
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
begin
soundentry:=hk.AppendChild(doc.CreateElement('ActivateSound'));;
soundentry.TextContent:=hotkey[i].activateSound;
if hotkey[i].ActivateSoundFlag<>hksPlaySound then
begin
a:=doc.CreateAttribute('TTS');
if hotkey[i].ActivateSoundFlag=hksSpeakTextEnglish then
a.TextContent:='EN'
else
a.TextContent:='';
soundentry.Attributes.SetNamedItem(a);
end;
end;
if hotkey[i].deactivateSound<>'' then
begin
soundentry:=hk.AppendChild(doc.CreateElement('DeactivateSound'));
soundentry.TextContent:=hotkey[i].deactivateSound;
if hotkey[i].DeactivateSoundFlag<>hksPlaySound then
begin
a:=doc.CreateAttribute('TTS');
if hotkey[i].DeactivateSoundFlag=hksSpeakTextEnglish then
a.TextContent:='EN'
else
a.TextContent:='';
soundentry.Attributes.SetNamedItem(a);
end;
end;
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 jni} treenode.Update; {$ENDIF}
end;
procedure TMemoryRecord.setShowAsSigned(state: boolean);
begin
fShowAsSignedOverride:=true;
fShowAsSigned:=state;
refresh;
end;
function TMemoryRecord.GetShowAsSigned: boolean;
begin
{$IFNDEF jni}
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
if (index>=0) and (index<length(fpointeroffsets)) then
result:=fpointeroffsets[index]
else
result:=nil;
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 jni}
result:=treenode.AbsoluteIndex;
{$ENDIF}
end;
procedure TMemoryRecord.setID(i: integer);
{$IFNDEF jni}
var a: TAddresslist;
{$ENDIF}
begin
{$IFNDEF jni}
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; onlyWhileDown: boolean=false): 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;
hk.fOnlyWhileDown:=onlyWhileDown;
hk.RegisterKeys;
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 [vtSingle, vtDouble]) or
((VarType=vtCustom) and (customtype<>nil) and customtype.scriptUsesFloat) then
begin
oldvaluedouble:=StrToFloat(getValue);
increasevalueDouble:=StrToFloatEx(value);
setvalue(FloatToStr(oldvaluedouble+increasevalueDouble));
end
else
begin
if (VarType=vtCustom) and (customtype.scriptUsesString) then exit;
oldvalue:=StrToQWordEx(getvalue);
increasevalue:=StrToQWordEx(value);
setvalue(IntToStr(oldvalue+increasevalue));
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, vtCustom] 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 [vtSingle, vtDouble]) or
((VarType=vtCustom) and (customtype<>nil) and customtype.scriptUsesFloat) then
begin
oldvaluedouble:=StrToFloat(getValue);
decreasevalueDouble:=StrToFloatEx(value);
setvalue(FloatToStr(oldvaluedouble-decreasevalueDouble));
end
else
begin
if (VarType=vtCustom) and (customtype.scriptUsesString) then exit;
oldvalue:=StrToQWordEx(getvalue);
decreasevalue:=StrToQWordEx(value);
setvalue(IntToStr(oldvalue-decreasevalue));
end;
end;
except
end;
end;
end;
procedure TMemoryRecord.disablewithoutexecute;
begin
{$IFNDEF jni}
factive:=false;
SetVisibleChildrenState;
treenode.Update;
{$ENDIF}
end;
procedure TMemoryRecord.DoHotkeyDisable(hk :TMemoryRecordHotkey); //disable the specific hotkey
begin
if (hk<>nil) and (hk.owner=self) and (hk.OnlyWhileDown) then
begin
try
case hk.Action of
mrhActivate, mrhToggleActivation, mrhToggleActivationAllowIncrease, mrhToggleActivationAllowDecrease: active:=false;
mrhSetValue: setvalue(hk.fValueAtActivation);
end;
except
end;
end;
end;
procedure TMemoryRecord.DoHotkey(hk: TMemoryRecordhotkey);
var oldstate: boolean;
begin
if (hk<>nil) and (hk.owner=self) and ((not hk.OnlyWhileDown) or (hk.OnlyWhileDown and (hk.down=false)) ) then
begin
try
case hk.action of
mrhToggleActivation:
begin
if (VarType=vtAutoAssembler) then
fScriptHotKey:=hk;
active:=not active;
if (VarType<>vtAutoAssembler) then
begin
if active then
hk.playActivateSound
else
hk.playDeactivateSound;
end;
end;
mrhSetValue:
begin
if hk.down=false then
hk.fValueAtActivation:=GetValue;
SetValue(hk.value);
hk.playActivateSound;
end;
mrhIncreaseValue:
begin
increaseValue(hk.value);
hk.playActivateSound;
end;
mrhDecreaseValue:
begin
decreaseValue(hk.value);
hk.playActivateSound;
end;
mrhToggleActivationAllowDecrease:
begin
if (VarType=vtAutoAssembler) then
fScriptHotKey:=hk;
allowDecrease:=True;
active:=not active;
if (VarType<>vtAutoAssembler) then
begin
if active then
hk.playActivateSound
else
hk.playDeactivateSound; //also gives a signal when failing to activate
end;
end;
mrhToggleActivationAllowIncrease:
begin
if (VarType=vtAutoAssembler) then
fScriptHotKey:=hk;
allowIncrease:=True;
active:=not active;
if (VarType<>vtAutoAssembler) then
begin
if active then
hk.playActivateSound
else
hk.playDeactivateSound; //also gives a signal when failing to activate
end;
end;
mrhActivate:
begin
if (VarType=vtAutoAssembler) then
fScriptHotKey:=hk;
active:=true;
if (VarType<>vtAutoAssembler) and active then
hk.playActivateSound;
end;
mrhDeactivate:
begin
if (VarType=vtAutoAssembler) then
fScriptHotKey:=hk;
active:=false;
if (VarType<>vtAutoAssembler) and (not active) then
hk.playDeactivateSound; //also gives a signal when failing to activate
end;
end;
except
//don't complain about incorrect values
end;
end;
{$IFNDEF jni}
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 jni}
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 jni}
treenode.update;
{$ENDIF}
end;
function TMemoryRecord.isProcessing: boolean;
begin
result:=processingThread<>nil;
end;
function TMemoryRecord.getProcessingTime: qword;
begin
if isProcessing then
result:=GetTickCount64-processingTimeStart
else
result:=0;
end;
function TMemoryRecord.hasMouseOver: boolean;
begin
result:=taddresslist(fowner).MouseHighlightedRecord=self;
end;
procedure TMemoryRecord.processingDone;
//called after an aa script has finished processing
var i: integer;
begin
if not fActive 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;
if processingThread<>nil then
freeandnil(processingThread);
{$IFNDEF jni}
treenode.update;
{$ENDIF}
{$IFNDEF jni}
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;
{$ENDIF}
//6.5+
{$ifndef jni}
LUA_functioncall('onMemRecPostExecute',[self, wantedstate, fActive=wantedstate]);
{$endif}
//6.1+
if wantedstate and assigned(fonactivate) then fonactivate(self, false, factive); //activated , after
if not wantedstate and assigned(fondeactivate) then fondeactivate(self, false, factive); //deactivated , after
SetVisibleChildrenState;
if fScriptHotKey<>nil then
begin
//play sounds if needed
if active then
fScriptHotKey.playActivateSound
else
fScriptHotKey.playDeactivateSound;
fScriptHotKey:=nil;
end;
end;
procedure TMemoryRecord.setActive(state: boolean);
var f: string;
i: integer;
p: boolean;
begin
{$ifdef windows}
if state and aprilfools then decreaseCheatECoinCount;
{$endif}
if state=fActive then exit; //no need to execute this is it's the same state
if processingThread<>nil then exit; //don't change the state while processing
// 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+
{$ifndef jni}
LUA_functioncall('onMemRecPreExecute',[self, state]);
{$endif}
//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;
wantedstate:=state;
if (state=false) 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);
if async then
processingTimeStart:=gettickcount64;
//and wait for them to finish
for i:=0 to treenode.count-1 do
begin
while TMemoryRecord(treenode[i].data).isProcessing do
begin
if async then
begin
processingThread:=TMemoryRecordProcessingThread(1); //fake it
application.ProcessMessages;
end;
CheckSynchronize(100);
// TMemoryRecord(treenode[i].data).treenode.Update;
Taddresslist(fOwner).Repaint;
end;
end;
processingThread:=nil;
end;
if not fisGroupHeader then
begin
if self.VarType = vtAutoAssembler then
begin
{$IFNDEF jni}
//aa script
if (state=true) and (autoassemblerdata.disableinfo<>nil) then
freeandnil(autoassemblerdata.disableinfo);
if autoassemblerdata.disableinfo=nil then
autoassemblerdata.disableinfo:=TDisableInfo.create;
autoassemblerdata.disableinfo.ccodesymbols.name:='Memoryrecord '+intTostr(id)+':'+Description;
if async then
begin
//spawn a thread to activate this entry.
//set the state to "Activating"
processingTimeStart:=gettickcount64;
processingThread:=TMemoryRecordProcessingThread.Create(self,state);
treenode.update;
exit;
end
else
begin
try
if autoassemble(autoassemblerdata.script, false, state, false, false, autoassemblerdata.disableinfo, self) then
begin
fActive:=state;
if (autoassemblerdata.disableinfo.registeredsymbols.Count>0) or (autoassemblerdata.disableinfo.ccodesymbols.Count>0) then //if it has a registered symbol then reinterpret all addresses
TAddresslist(fOwner).ReinterpretAddresses;
autoassemblerdata.lastExecutionFailed:=false;
end
else
begin
autoassemblerdata.lastExecutionFailed:=true;
autoassemblerdata.lastExecutionFailedReason:=rsUnknown;
end;
except
//running the script failed, state unchanged
on e:exception do
begin
autoassemblerdata.lastExecutionFailed:=true;
autoassemblerdata.lastExecutionFailedReason:=e.message;
end;
end;
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;
processingDone;
end;
procedure TMemoryRecord.setDescription(d: string);
begin
TAddresslist(fowner).MemrecDescriptionChange(self, fdescription, d);
fdescription:=d;
end;
procedure TMemoryRecord.setVisible(state: boolean);
begin
fVisible:=state;
{$IFNDEF jni}
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 jni}
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
fCustomType:=GetCustomTypeFromName(fCustomTypeName);
end;
function TMemoryRecord.parseAddressString(s: string; out address: ptruint): boolean;
//parses the addressString based on the memoryrecord it's in (relative offsets are memrec dependant)
var
err: boolean;
relative: boolean;
parentmr: TMemoryrecord;
begin
result:=false;
address:=symhandler.getAddressFromName(s,false,err);
result:=not err;
if result then
begin
s:=trim(s);
if hasparent and ((s<>'') and (s[1] in ['+','-'])) then
begin
//relative
parentMR:=parent;
while ((parentMR.interpretableaddress='') or (parentMR.interpretableaddress='0')) and parentMR.hasParent do parentMR:=parentMR.parent; // find first ancestor with interpretableaddress
if not ((parentMR.interpretableaddress='') or (parentMR.interpretableaddress='0')) then
address:=parentMR.RealAddress+address; //assuming that the ancestor has had it's real address calculated first
end;
end;
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;
for i:=0 to offsetCount-1 do
begin
if offsets[i].unparsed then
offsets[i].offsetText:=offsets[i].offsetText;
if offsets[i].invalid or offsets[i].OnlyUpdateWithReinterpret then
offsets[i].forced:=true;
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:=StrToQWordEx('$'+newvalue);
olddecimalvalue:=StrToQWordEx('$'+oldvalue);
end
else
begin
newdecimalvalue:=StrToQWordEx(newvalue);
olddecimalvalue:=StrToQWordEx(oldvalue);
end;
if (allowIncrease and ShowAsSigned and (int64(newdecimalvalue)>int64(olddecimalvalue))) or
(allowIncrease and (not ShowAsSigned) and (newdecimalvalue>olddecimalvalue)) or
(allowDecrease and (ShowAsSigned) and (int64(newdecimalvalue)<int64(olddecimalvalue))) or
(allowDecrease and (not ShowAsSigned) 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;
found: boolean;
hasNotFoundResult: boolean;
notfoundresult: string;
begin
result:=getValue;
if assigned(fOnGetDisplayValue) and fOnGetDisplayValue(self, result) then exit;
c:=DropDowncount;
if getDisplayAsDropDownListItem and (c>0) then
begin
notfoundresult:='';
found:=false;
hasNotFoundResult:=false;
//convert the value to a dropdown list item value
for i:=0 to c-1 do
begin
if DropDownReadOnly and DropDownDescriptionOnly and DisplayAsDropDownListItem and (DropDownValue[i]='*') then
begin
hasNotFoundResult:=true;
notfoundresult:=DropDownDescription[i];
end;
if uppercase(utf8toansi(DropDownValue[i]))=uppercase(result) then
begin
found:=true;
if getDropDownDescriptionOnly then
result:=utf8toansi(DropDownDescription[i])
else
result:=result+' : '+utf8toansi(DropDownDescription[i]);
end;
end;
if (not found) and DropDownReadOnly and DropDownDescriptionOnly and DisplayAsDropDownListItem and hasNotFoundResult then
result:=notfoundresult;
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;
f: single;
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 fcustomtype<>nil then
begin
if fCustomType.scriptUsesString then
begin
result:=fCustomType.ConvertDataToString(buf, realaddress);
end
else
if fcustomtype.scriptUsesFloat then
begin
if ShowAsHex then //so stupid, but whatever
begin
f:=fcustomtype.ConvertDataToFloat(buf, RealAddress);
result:=inttohex(pdword(@f)^,8);
end
else
result:=FloatToStr(fcustomtype.ConvertDataToFloat(buf, RealAddress))
end
else
if showashex then result:=inttohex(fcustomtype.ConvertDataToInteger(buf, RealAddress),8)
else if showassigned then result:=inttostr(integer(fcustomtype.ConvertDataToInteger(buf, RealAddress)))
else result:=inttostr(dword(fcustomtype.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:=UTF16ToUTF8(wc)
end
else
if Extra.stringData.codepage then
begin
result:=WinCPToUTF8(c);
end
else
result:=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;
freememandnil(buf);
if assigned(fOnValueChanged) and (result<>LastSeenValue) then
fOnValueChanged(self, LastSeenValue, result);
LastSeenValue:=result;
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;
v64: qword;
bts: TBytes;
mask: qword;
temp: qword;
temps: string;
tempsw: widestring;
tempsa: ansistring;
mr: TMemoryRecord;
unparsedvalue, parsedvalue: string;
check: boolean;
oldluatop: integer;
vpe: boolean=false;
setvaluescript: Tstringlist;
usesMath: boolean;
lastBraceOpen: integer;
f: single;
newundovalue: string;
suspended: boolean;
begin
//check if it is a '(description)' notation
unparsedvalue:=v;
if vartype<>vtString then
begin
v:=trim(v);
{$IFNDEF jni}
if vartype in [vtByte..vtDouble, vtCustom] then
begin
//numeric type: scan for (...) and replace it with the apropriate values
//then apply the magic of math/lua to the what is left (assuming math is used, which is found out during the first pass scan)
usesmath:=false;
if (length(v)>1) and (v[1]<>'[') then //not a legacy lua specific value
begin
lastBraceOpen:=0;
parsedvalue:='';
i:=1;
while i<=length(v) do
begin
case v[i] of
'(':
begin
if lastBraceOpen>0 then usesMath:=true;
lastBraceOpen:=i;
end;
')':
begin
if lastBraceOpen>0 then
begin
temps:=copy(v,lastBraceOpen+1,i-lastBraceOpen-1);
mr:=TAddresslist(fOwner).getRecordWithDescription(temps);
if mr<>nil then
begin
//replace the (memrecdescription) with the memrecvalue
temps:=mr.getValue;
if mr.ShowAsHex then
begin
if ShowAsHex=false then
temps:='0x'+temps;
if VarType in [vtSingle, vtDouble, vtCustom] then
begin
if (vartype<>vtCustom) or (fcustomtype.scriptUsesFloat) then
begin
//handle it as an actual float(why the fuck would anyone put a float as hex...)
try
v64:=strtoint64(temps);
buf:=@v64;
if vartype in [vtSingle, vtCustom] then
temps:=FloatToStr(ps^)
else
temps:=FloatToStr(pd^);
except
//ugh... whatever
end;
end;
end;
end;
v:=copy(v,1,lastBraceOpen-1)+temps+copy(v,i+1);
i:=lastBraceOpen+length(temps);
lastBraceOpen:=0;
continue;
end
else
usesMath:=true;
lastBraceOpen:=0;
end else usesMath:=true; //weird math that I don't get and should fail, but whatever...
end;
'-','+','/','*': if i>1 then usesMath:=true;
end;
inc(i);
end;
if usesmath then
v:='['+v+']'; //send it to the lua parser
end;
end
else
begin
//not an integer type, can still use the notation though
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;
end;
{$ENDIF}
end;
if (not isfreezer) then
newundovalue:=GetValue;
realAddress:=GetRealAddress; //quick update
{$IFNDEF jni}
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
if fisGroupHeader then exit;
currentValue:={utf8toansi}(v);
if fShowAsHex and (not (vartype in [vtSingle, vtDouble, vtByteArray, vtString] )) then
begin
if length(currentvalue)>0 then
begin
currentvalue:=trim(currentValue);
if copy(currentvalue,1,3)='-0x' then
currentvalue:='-'+copy(currentvalue,4)
else
if copy(currentvalue,1,2)='0x' then
currentvalue:=copy(currentvalue,3);
if currentvalue[1]='-' then
begin
currentvalue:='-$'+copy(currentvalue,2,length(currentvalue));
end
else
currentvalue:='$'+currentvalue;
end;
end;
bufsize:=4+getbytesize; //+4 because of 1 byte custom types that may show as a hexadecimal float.. ugh.. why...
if (vartype=vtbinary) and (bufsize=3) then bufsize:=4;
if (vartype=vtbinary) and (bufsize>4) then bufsize:=8;
getmem(buf,bufsize+2);
suspended:=false;
if SystemSupportsWritableExecutableMemory or SkipVirtualProtectEx then
begin
vpe:=(SkipVirtualProtectEx=false) and VirtualProtectEx(processhandle, pointer(realAddress), bufsize, PAGE_EXECUTE_READWRITE, originalprotection);
end
else
begin
if (SkipVirtualProtectEx=false) and (iswritable(realaddress)=false) then
begin
if processid<>GetCurrentProcessId then
begin
ntsuspendProcess(processhandle);
suspended:=true;
end;
vpe:=(SkipVirtualProtectEx=false) and VirtualProtectEx(processhandle, pointer(realAddress), bufsize, PAGE_READWRITE, originalprotection);
end;
end;
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 jni}
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
oldluatop:=lua_gettop(luavm);
setvaluescript:=tstringlist.create;
try
setvaluescript.Add('local oldvalue='+getValue);
setvaluescript.Add('local value=oldvalue');
setvaluescript.Add('return '+copy(CurrentValue,2, length(CurrentValue)-2));
if lua_dostring(luavm, pchar(setvaluescript.text))=0 then
currentValue:=lua_tostring(luavm, -1);
finally
lua_settop(luavm, oldluatop);
freeandnil(setvaluescript);
end;
end;
end;
{$ENDIF}
case VarType of
vtCustom:
begin
if fcustomtype<>nil then
Begin
if fcustomtype.scriptUsesString then
begin
fCustomType.ConvertStringToData(pchar(v), pb, RealAddress); //utf8 format
end
else
if fcustomtype.scriptUsesFloat then
begin
if not fShowAsHex then
fcustomtype.ConvertFloatToData(StrToFloatEx(currentValue), ps, RealAddress)
else
begin
v64:=StrToQWordEx(currentvalue); //hexvalue yeah...
fcustomtype.ConvertFloatToData(psingle(@v64)^, ps, RealAddress)
end;
end
else
fcustomtype.ConvertIntegerToData(StrToQWordEx(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
ps^:=StrToFloatEx(currentValue);
vtDouble: if (not fShowAsHex) or (not TryStrToQWord('$'+currentvalue, li64^)) then
pd^:=StrToFloatEx(currentValue);
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);
freememandnil(buf);
bufsize:=getbytesize+2;
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
tempsw:=currentvalue;
tempsa:=currentvalue;
//copy the string to the buffer
if extra.stringData.unicode then
begin
x:=min(x,PtrUInt(length(tempsw)));
if extra.stringData.ZeroTerminate then
inc(x); //include the zero terminator
if length(tempsw)=0 then
zeromemory(buf, x*2)
else
CopyMemory(buf, @tempsw[1], x*2)
end
else
begin
if extra.stringData.codepage then
tempsa:=UTF8ToWinCP(tempsa);
x:=min(x,PtrUInt(length(tempsa)));
if extra.stringData.ZeroTerminate then
inc(x); //include the zero terminator
if length(tempsa)=0 then
zeromemory(buf, x)
else
CopyMemory(buf, @tempsa[1], x);
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);
freememandnil(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
if vpe then
VirtualProtectEx(processhandle, pointer(realAddress), bufsize, originalprotection, originalprotection);
if suspended then
ntresumeProcess(processhandle);
end;
freememandnil(buf);
frozenValue:=unparsedvalue; //we got till the end, so update the frozen value
if (not isfreezer) then
begin
if (GetValue<>newundovalue) then
undovalue:=newundovalue;
if assigned(fOnValueChangedByUser) then
fOnValueChangedByUser(self, newundovalue, LastSeenValue);
end;
end;
function TMemoryRecord.getBaseAddress: ptrUint;
var parentMR: TMemoryRecord;
begin
if fIsOffset and hasParent then
begin
parentMR:=parent;
while ((parentMR.interpretableaddress='') or (parentMR.interpretableaddress='0')) and parentMR.hasParent do parentMR:=parentMR.parent; // find first ancestor with interpretableaddress
if not ((parentMR.interpretableaddress='') or (parentMR.interpretableaddress='0')) then
result:=parentMR.RealAddress+baseaddress //assuming that the ancestor has had it's real address calculated first
else
result:=BaseAddress;
end
else
result:=BaseAddress;
end;
function TMemoryRecord.GetRealAddress: PtrUInt;
var
check: boolean;
realaddress, realaddress2: PtrUInt;
i: integer;
count: ptruint;
o: integer;
begin
realAddress:=0;
if isPointer then //it's a pointer
begin
realAddress2:=getBaseAddress;
for i:=offsetCount-1 downto 0 do
begin
check:=readprocessmemory(processhandle,pointer(realaddress2),@realaddress,processhandler.pointersize,count);
if check and (count=processhandler.pointersize) then
begin
o:=offsets[i].getOffset(realaddress);
if not offsets[i].invalid then
realaddress2:=realaddress+o
else
begin
UnreadablePointer:=true;
self.RealAddress:=0;
exit(0);
end;
end
else
begin
UnreadablePointer:=true;
self.RealAddress:=0;
exit(0);
end;
end;
UnreadablePointer:=false;
result:=realaddress2;
end
else
result:=getBaseAddress; //not a pointer
self.RealAddress:=result;
end;
function TMemoryRecord.getlinkedDropDownMemrec: TMemoryRecord;
var leng: integer;
begin
if linkedDropDownMemrec=nil then
begin
linkedDropDownMemrec:=TAddresslist(fOwner).getRecordWithDescription(fDropDownLinkedMemrec);
if (linkedDropDownMemrec<>nil) and
(linkedDropDownMemrec.getlinkedDropDownMemrec_LoopDetected=false) then
begin
leng:=length(linkedDropDownMemrec.memrecsLinkedToMe);
setlength(linkedDropDownMemrec.memrecsLinkedToMe, leng+1);
linkedDropDownMemrec.memrecsLinkedToMe[leng]:=self;
end
else
linkedDropDownMemrec:=nil;
end;
result:=linkedDropDownMemrec;
end;
function TMemoryRecord.getlinkedDropDownMemrec_LoopDetected: boolean;
var mr_slow,mr_fast: TMemoryRecord;
begin
result:=false;
mr_slow:=linkedDropDownMemrec;
mr_fast:=linkedDropDownMemrec;
// Floyds Cycle-Finding Algorithm
while (mr_slow<>nil) and (mr_fast<>nil) and (mr_fast.getlinkedDropDownMemrec<>nil) do
begin
mr_slow:=mr_slow.getlinkedDropDownMemrec;
mr_fast:=mr_fast.getlinkedDropDownMemrec;
mr_fast:=mr_fast.getlinkedDropDownMemrec;
if mr_slow=mr_fast then exit(true);
end;
end;
function MemRecHotkeyActionToText(action: TMemrecHotkeyAction): string;
begin
result:='';
//DO NOT TRANSLATE THIS
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
//DO NOT TRANSLATE THIS
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.