cheat-engine/memscan.pas
cheatengine@gmail.com 4d297adfd2
2011-07-04 19:52:19 +00:00

4309 lines
137 KiB
ObjectPascal

unit memscan;
{
This unit will hold the class object used to control scanning
The old scanning routines will be moved out of cefuncproc and made object oriented into this class
Special care should be taken to add multithreaded scanning routines
}
interface
uses windows,sysutils, classes,ComCtrls,dialogs, cefuncproc,
newkernelhandler, math, SyncObjs
{$ifndef standalonetrainerwithassemblerandaobscanner}
, windows7taskbar,SaveFirstScan, firstscanhandler, autoassembler, symbolhandler
{$endif}
;
type TCheckRoutine=function(newvalue,oldvalue: pointer):boolean of object;
type TStoreResultRoutine=procedure(address: dword; oldvalue: pointer) of object;
type TFlushRoutine=procedure of object;
type
TMemScan=class;
TScanController=class;
TScanner=class;
Tscanfilewriter=class(tthread)
{
workerthread:
This class is used because it is more efficient to write only one file at a
time. It will aquire a critical section when writing a file to acomplish that
also, not that it matters much, since you eventually have to wait for the
write anyhow, a double buffered result list so it can keep scanning while the
list is being saved.
}
private
scancontroller: TScanController;
scanner: TScanner;
addressfile: TFilestream;
memoryfile: TFilestream;
// cAddressFile,cMemoryFile: TCompressionStream;
addressbuffer: pointer;
memorybuffer: pointer;
addressSize: dword;
memorySize: dword;
datawritten: tevent; //event that is set when the thread has finished writing
dataavailable:tevent; //event that is set when there is a buffer to save
public
writeError: boolean; //gets set if an exception happened
errorString: string; //exception description
procedure execute; override;
procedure writeresults(addressbuffer,memorybuffer: pointer; addressSize,memorySize: dword); //writes the results of address and memory
procedure flush;
constructor create(scanner: TScanner; scancontroller:TScanController; addressfile,memoryfile:TFileStream);
destructor destroy; override;
end;
TScanner=class(tthread)
{
The scanner class will scan a specified range of memory
}
private
CheckRoutine: TCheckRoutine;
StoreResultRoutine: TStoreResultRoutine;
FlushRoutine: TFlushRoutine; //pointer to routine used to flush the buffer, generic, string, etc...
scanWriter: Tscanfilewriter;
{$ifndef standalonetrainerwithassemblerandaobscanner}
firstscanhandler: TFirstscanhandler;
{$endif}
customprologue: procedure; stdcall; //customscan call before scan starts
customepilogue: procedure; stdcall; //customscan call after scan ends
found :dword;
value,value2: int64;
svalue,svalue2: single;
dvalue,dvalue2: double;
minsvalue,maxsvalue: single;
mindvalue,maxdvalue: double;
floataccuracy: integer; //number of digits after the decimal seperator
CurrentAddressBuffer: pointer;
CurrentFoundBuffer: pointer; //generic buffer that points to where the memory can be found
SecondaryFoundBuffer: pointer; //current gets swapped with these ones after a write
SecondaryAddressBuffer: pointer;
//binary scan
bitmask: int64; //contains the bit string where * is replaced with a 0
andmask: int64; //contains the bit string where 1 and 0 is 1 and * is 0
binaryresults: array [0..7] of boolean; //after a byte is checked this contains which startbits match
//array of bytes
abs_arraylength: integer; //optimization so no need to call length()
abs_arraytofind: TBytes;
//all
typesmatch: array [vtByte..vtDouble] of boolean; //will get set depending if that type matches the current address or not
//check routines:
function ByteExact(newvalue,oldvalue: pointer): boolean;
function ByteBetween(newvalue,oldvalue: pointer): boolean;
function ByteBiggerThan(newvalue,oldvalue: pointer): boolean;
function ByteSmallerThan(newvalue,oldvalue: pointer): boolean;
function ByteIncreasedValue(newvalue,oldvalue: pointer): boolean;
function ByteIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function ByteIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function ByteDecreasedValue(newvalue,oldvalue: pointer): boolean;
function ByteDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function ByteDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function ByteChanged(newvalue,oldvalue: pointer): boolean;
function ByteUnChanged(newvalue,oldvalue: pointer): boolean;
function WordExact(newvalue,oldvalue: pointer): boolean;
function WordBetween(newvalue,oldvalue: pointer): boolean;
function WordBiggerThan(newvalue,oldvalue: pointer): boolean;
function WordSmallerThan(newvalue,oldvalue: pointer): boolean;
function WordIncreasedValue(newvalue,oldvalue: pointer): boolean;
function WordIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function WordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function WordDecreasedValue(newvalue,oldvalue: pointer): boolean;
function WordDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function WordDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function WordChanged(newvalue,oldvalue: pointer): boolean;
function WordUnChanged(newvalue,oldvalue: pointer): boolean;
function DWordExact(newvalue,oldvalue: pointer): boolean;
function DWordBetween(newvalue,oldvalue: pointer): boolean;
function DWordBiggerThan(newvalue,oldvalue: pointer): boolean;
function DWordSmallerThan(newvalue,oldvalue: pointer): boolean;
function DWordIncreasedValue(newvalue,oldvalue: pointer): boolean;
function DWordIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function DWordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function DWordDecreasedValue(newvalue,oldvalue: pointer): boolean;
function DWordDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function DWordDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
function DwordChanged(newvalue,oldvalue: pointer): boolean;
function DwordUnChanged(newvalue,oldvalue: pointer): boolean;
function QWordExact(newvalue,oldvalue: pointer): boolean;
function QWordBetween(newvalue,oldvalue: pointer): boolean;
function QWordBiggerThan(newvalue,oldvalue: pointer): boolean;
function QWordSmallerThan(newvalue,oldvalue: pointer): boolean;
function QWordIncreasedValue(newvalue,oldvalue: pointer): boolean;
function QWordIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function QWordIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function QWordDecreasedValue(newvalue,oldvalue: pointer): boolean;
function QWordDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function QWordDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function QWordChanged(newvalue,oldvalue: pointer): boolean;
function QwordUnChanged(newvalue,oldvalue: pointer): boolean;
function SingleExact(newvalue,oldvalue: pointer): boolean;
function SingleBetween(newvalue,oldvalue: pointer): boolean;
function SingleBiggerThan(newvalue,oldvalue: pointer): boolean;
function SingleSmallerThan(newvalue,oldvalue: pointer): boolean;
function SingleIncreasedValue(newvalue,oldvalue: pointer): boolean;
function SingleIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function SingleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function SingleDecreasedValue(newvalue,oldvalue: pointer): boolean;
function SingleDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function SingleDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function SingleChanged(newvalue,oldvalue: pointer): boolean;
function singleUnChanged(newvalue,oldvalue: pointer): boolean;
function DoubleExact(newvalue,oldvalue: pointer): boolean;
function DoubleBetween(newvalue,oldvalue: pointer): boolean;
function DoubleBiggerThan(newvalue,oldvalue: pointer): boolean;
function DoubleSmallerThan(newvalue,oldvalue: pointer): boolean;
function DoubleIncreasedValue(newvalue,oldvalue: pointer): boolean;
function DoubleIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function DoubleIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function DoubleDecreasedValue(newvalue,oldvalue: pointer): boolean;
function DoubleDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function DoubleDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function DoubleChanged(newvalue,oldvalue: pointer): boolean;
function DoubleUnChanged(newvalue,oldvalue: pointer): boolean;
function AllExact(newvalue,oldvalue: pointer):boolean; //check byte,word,dword,qword,single and float
function AllBetween(newvalue,oldvalue: pointer): boolean;
function AllBiggerThan(newvalue,oldvalue: pointer): boolean;
function AllSmallerThan(newvalue,oldvalue: pointer): boolean;
function AllIncreasedValue(newvalue,oldvalue: pointer): boolean;
function AllIncreasedValueBy(newvalue,oldvalue: pointer): boolean;
function AllIncreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function AllDecreasedValue(newvalue,oldvalue: pointer): boolean;
function AllDecreasedValueBy(newvalue,oldvalue: pointer): boolean;
function AllDecreasedValueByPercentage(newvalue,oldvalue: pointer): boolean;
function AllChanged(newvalue,oldvalue: pointer): boolean;
function AllUnchanged(newvalue,oldvalue: pointer): boolean;
//following types only have exact: Array of byte, binary and string
function ArrayOfByteExact(newvalue,oldvalue: pointer):boolean;
function BinaryExact(newvalue,oldvalue: pointer):boolean;
function CaseSensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean;
function CaseInsensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean;
function CaseSensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean;
function CaseInsensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean;
//save macthing address routines:
procedure GenericSaveResult(address: dword; oldvalue: pointer); //only use as last resort : Call to copymemory just to store one entry
procedure allSaveResult(address: dword; oldvalue: pointer);
procedure binarySaveResult(address: dword; oldvalue: pointer);
procedure arrayOfByteSaveResult(address: dword; oldvalue: pointer);
procedure ByteSaveResult(address: dword; oldvalue: pointer);
procedure WordSaveResult(address: dword; oldvalue: pointer);
procedure DWordSaveResult(address: dword; oldvalue: pointer);
procedure QWordSaveResult(address: dword; oldvalue: pointer);
procedure SingleSaveResult(address: dword; oldvalue: pointer);
procedure DoubleSaveResult(address: dword; oldvalue: pointer);
//flush routines
procedure genericFlush; //generic routine for flushing the buffer
procedure stringFlush; //don't save the memory
procedure binaryFlush; //don't save memory AND use foundaddressb for results
procedure allFlush;
procedure configurescanroutine; //parses scanvalue1,2 according to the VariableType and scanoptions
procedure FirstScanmem(base:dword; buffer: pointer; size: integer); //routine that gets a buffer and saves all the results it finds in the buffer (For firstscan)
procedure FirstNextScanmem(base:dword; buffer,oldbuffer: pointer; size: integer);
procedure nextnextscanmemall(addresslist: pointer; oldmemory: pointer; chunksize: integer);
procedure nextnextscanmembinary(addresslist: pointer; chunksize: integer);
procedure nextnextscanmem(addresslist: pointer; oldmemory: pointer; chunksize: integer);
procedure firstscan; //copy the given range to the memory region
procedure firstnextscan; //routine used when the results list contains nothing but a indicator a unknown scan was done
procedure nextnextscan; //routine used when the results list contains addresses
public
OwningScanController: TScanController;
Addressfile: TFilestream; //CheatEngineDir+'Addresses'+ThreadID.TMP'
MemoryFile: TFileStream; //CheatEngineDir+'Memory'+ThreadID.TMP'
Addressfilename: string;
MemoryFilename: string;
percentage: boolean;
roundingtype: TRoundingtype;
hexadecimal: boolean;
binaryStringAsDecimal: boolean;
readonly: boolean;
fastscan: boolean;
unicode: boolean;
caseSensitive: boolean;
fastscanalignsize: integer;
variablesize: integer;
scanvalue1,scanvalue2: string;
widescanvalue1: widestring;
scanOption: TScanOption;
variableType: TVariableType;
scanType: TScanType; //defines if it's a firstscan or next scan. (newscan is ignored)
useNextNextscan: boolean; //determines to use the nextNextScan or firstNextScan
customscanscript: tstrings; //holds the info for the custom type, script for AA, script for CScript, 2 lines for dll: dllname+function
customscantype: TCustomScanType;
customscanAllocArray: TCEAllocArray;
//thread controlling variables:
isdone: boolean; //will get set to true when the thread finishes normally
haserror: boolean;
errorstring: string;
//region related scans:
//startregion and stopregion
_startregion: integer;
_stopregion: integer;
maxregionsize: dword; //max size of buffer to be allocated when not unknown scan
//recreated memory region list for this specific range, can be used to see which regions where only half read
memRegions: TMemoryregions;
memRegionPos: integer;
startaddress: dword; //specific start for this this thread
stopaddress: dword; //specific stop for this thread, if not fastscan and another thread continue from here, may add some overlopping bytes
//exact address scans:
startentry: integer; //index in the address list
stopentry: integer; //" "
//general:
scanned: dword; //total memory/addresses scanned by this routine
totalfound: dword;
OnlyOne: boolean;
AddressFound: dword;
scannernr: integer;
procedure execute; override;
constructor create(suspended: boolean);
destructor destroy; override;
end;
TScanController=class(tthread)
{
The ScanController will configure the scanners and wait till they are done, mainly a idle thread
}
private
threadcount: integer;
addressFile: TFileStream;
memoryFile: TFileStream;
savescannerresults: boolean; //tells the epilogue to save the results to addressfile and memoryfile
isdoneEvent: TEvent; //gets set when the scan has finished
procedure updategui;
procedure errorpopup;
procedure firstScan;
procedure nextScan;
procedure firstnextscan; //rotine used when the results list contains nothing but a indicator a unknown scan was done
procedure nextnextscan; //routine used when the results list contains addresses
procedure fillVariableAndFastScanAlignSize; //fills in the variablesize and fastscanalignsizevariables, used for firstscan and firstnextscan
public
OwningMemScan: TMemScan;
resultsaveCS: tcriticalsection; //critical section the scanfilewriter objects use to determine if there is another scanthread writing
scannersCS: TCriticalSection; //Critican section used aty the end of the scancontroller thread, when the scanners are being destroyed
scanners: array of tscanner;
totalAddresses: dword; //how many addresses it will have to scan
percentage: boolean;
roundingtype: TRoundingType;
hexadecimal: boolean;
binaryStringAsDecimal: boolean;
readonly: boolean;
fastscan: boolean;
unicode: boolean;
casesensitive: boolean;
fastscanalignsize: integer;
variablesize: integer;
scanvalue1,scanvalue2: string;
startaddress: dword; //start for the whole scan
stopaddress: dword; //stop of the whole scan
scanOption: TScanOption;
variableType: TVariableType;
scanType: TScanType; //defines if it's a firstscan or next scan. (newscan is ignored)
customscanscript: tstrings; //holds the info for the custom type, script for AA, script for CScript, 2 lines for dll: dllname+function
customscantype: TCustomScanType;
//memregion info
memregion: TMemoryregions; //scanners have access to this, but make sure to NOT WRITE it
memRegionPos: Integer;
//thread controlling variables:
isdone: boolean; //will get set to true when the thread finishes normally
haserror: boolean;
errorstring: string;
//messages
notifywindow: thandle;
notifymessage: integer;
//OnlyOne vars
OnlyOne: boolean;
FoundSomething: Boolean;
AddressFound: Dword;
procedure execute; override;
constructor create(suspended: boolean);
destructor destroy; override;
end;
TMemScan=class
{
Configures the gui and related objects and launch TScanner objects with those objects
}
private
previousMemoryBuffer: pointer;
scanController: TScanController; //thread that configures the scanner threads and wait till they are done
{$ifndef standalonetrainerwithassemblerandaobscanner}
SaveFirstScanThread: TSaveFirstScanThread; //thread that will save the results of the first scan that can be used for "same as first scan" scans
{$endif}
memRegion: TMemoryRegions; //after a scan the contents of controller gets copied to here
memRegionPos: integer;
progressbar: TProgressBar;
notifywindow: thandle;
notifymessage: integer;
currentVariableType: TVariableType;
found: uint64;
//string stuff:
stringUnicode: boolean;
stringLength: integer;
//binary stuff:
binaryLength: integer;
//custom:
customvariablesize: integer;
//array stuff:
arrayLength: integer;
FLastScanType: TScanType;
public
onlyOne: boolean;
function GetProgress(var totaladdressestoscan:dword; var currentlyscanned: dword):integer;
function GetErrorString: string;
function GetFoundCount: uint64;
function Getbinarysize: int64; //returns the number of bits of the current type
function Getcustomvariablesize: integer;
function GetOnlyOneResult(var address: dword):boolean;
procedure TerminateScan(forceTermination: boolean);
procedure newscan; //will clean up the memory and files
procedure firstscan(scanOption: TScanOption; VariableType: TVariableType; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: dword; fastscan,readonly,hexadecimal,binaryStringAsDecimal,unicode,casesensitive,percentage: boolean; customscanscript: tstrings; customscantype: TCustomScanType);
procedure NextScan(scanOption: TScanOption; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: dword; fastscan,readonly,hexadecimal,binaryStringAsDecimal,unicode,casesensitive,percentage: boolean; customscanscript: tstrings; customscantype: TCustomScanType); //next scan, determine what kind of scan and give to firstnextscan/nextnextscan
procedure waittilldone;
procedure setScanDoneCallback(notifywindow: thandle; notifymessage: integer);
constructor create(progressbar: TProgressbar);
destructor destroy; override;
property LastScanType: TScanType read FLastScanType;
end;
implementation
uses StrUtils;
var foundaddress: byte;
foundaddressb: byte;
//===============Local functions================//
function getBytecountArrayOfByteString(st: string): integer;
var bytes: tbytes;
begin
ConvertStringToBytes(st,true,bytes);
result:=length(bytes);
end;
function getBytecountBinaryString(st:string; scanvalueisdecimal: boolean): integer;
var value: dword;
i: integer;
begin
if scanvalueisdecimal then //first convert do binarystring
st:=inttobin(strtoint(st));
result:=0;
for i:=1 to length(st) do
begin
case st[i] of
'0','1','?','*': inc(result);
' ',#8: ; //ignore
else raise exception.Create(st[i]+' is not a valid character inside a binary string');
end;
end;
if result=0 then raise exception.Create('Invalid binary notation');
if (result mod 8=0) then
result:=1+result div 8
else
result:=2+(result div 8);
end;
//==================TScanner===================//
//-----------=====Scanner check routines=====--------------//
function TScanner.AllExact(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteExact(newvalue,oldvalue); //oldvalue=nil, but give it anyhow
typesmatch[vtWord]:=typesmatch[vtWord] and WordExact(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordExact(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordExact(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleExact(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleExact(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllBetween(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteBetween(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordBetween(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordBetween(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordBetween(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleBetween(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleBetween(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllBiggerThan(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteBiggerThan(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordBiggerThan(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordBiggerThan(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordBiggerThan(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleBiggerThan(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleBiggerThan(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllSmallerThan(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteSmallerThan(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordSmallerThan(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordSmallerThan(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordSmallerThan(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleSmallerThan(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleSmallerThan(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllIncreasedValue(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteIncreasedValue(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordIncreasedValue(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordIncreasedValue(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordIncreasedValue(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleIncreasedValue(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleIncreasedValue(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteIncreasedValueBy(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordIncreasedValueBy(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordIncreasedValueBy(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordIncreasedValueBy(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleIncreasedValueBy(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleIncreasedValueBy(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteIncreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordIncreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordIncreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordIncreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleIncreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleIncreasedValueByPercentage(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllDecreasedValue(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteDecreasedValue(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordDecreasedValue(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordDecreasedValue(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordDecreasedValue(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleDecreasedValue(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleDecreasedValue(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteDecreasedValueBy(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordDecreasedValueBy(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordDecreasedValueBy(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordDecreasedValueBy(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleDecreasedValueBy(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleDecreasedValueBy(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteDecreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordDecreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordDecreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordDecreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleDecreasedValueByPercentage(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleDecreasedValueByPercentage(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.Allchanged(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteChanged(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordChanged(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordChanged(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordChanged(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleChanged(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleChanged(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.AllUnchanged(newvalue,oldvalue: pointer):boolean;
var i: TVariableType;
begin
typesmatch[vtByte]:=typesmatch[vtByte] and ByteUnchanged(newvalue,oldvalue);
typesmatch[vtWord]:=typesmatch[vtWord] and WordUnchanged(newvalue,oldvalue);
typesmatch[vtDword]:=typesmatch[vtDword] and DwordUnchanged(newvalue,oldvalue);
typesmatch[vtQword]:=typesmatch[vtQword] and qwordUnchanged(newvalue,oldvalue);
typesmatch[vtSingle]:=typesmatch[vtSingle] and singleUnchanged(newvalue,oldvalue);
typesmatch[vtDouble]:=typesmatch[vtDouble] and doubleUnchanged(newvalue,oldvalue);
result:=false;
for i:=vtbyte to vtdouble do
if typesmatch[i] then
begin
result:=true;
exit;
end;
end;
function TScanner.CaseSensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
begin
result:=false;
for i:=1 to length(scanvalue1) do
if scanvalue1[i]<>(pchar(newvalue)[i-1]) then exit;
result:=true;
end;
function TScanner.CaseInsensitiveAnsiStringExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
begin
//scanvalue1 has already been converted to uppercase in config
result:=false;
for i:=1 to length(scanvalue1) do
begin
if pchar(newvalue)[(i-1)] in ['a'..'z'] then //change to uppercase
dec(pbytearray(newvalue)[(i-1)],$20);
if scanvalue1[i]<>(pchar(newvalue)[i-1]) then exit;
end;
result:=true; //it got here, so a match
end;
function TScanner.CaseSensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
begin
result:=false;
for i:=1 to length(scanvalue1) do
if widescanvalue1[i]<>(pwidechar(newvalue)[i-1]) then exit;
result:=true;
end;
function TScanner.CaseInsensitiveUnicodeStringExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
scanvaluellength: integer;
pwidescanvalue: pchar;
begin
result:=false;
i:=0;
scanvaluellength:=length(scanvalue1)*sizeof(widechar);
pwidescanvalue:=@widescanvalue1[1];
for i:=1 to length(scanvalue1) do
begin
if pchar(newvalue)[(i-1)*sizeof(wchar)] in ['a'..'z'] then //change to uppercase
dec(pbytearray(newvalue)[(i-1)*sizeof(wchar)],$20);
if widescanvalue1[i]<>(pwidechar(newvalue)[i-1]) then exit;
end;
result:=true; //it got here, so a match
end;
function TScanner.ArrayOfByteExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
begin
for i:=0 to abs_arraylength-1 do
if (abs_arraytofind[i]<>-1) and (pbytearray(newvalue)[i]<>abs_arraytofind[i]) then
begin
result:=false; //no match
exit;
end;
result:=true; //still here, so a match
end;
function TScanner.BinaryExact(newvalue,oldvalue: pointer):boolean;
var i: integer;
begin
for i:=0 to 7 do
binaryresults[i]:=((puint64(newvalue)^ shr i) and andmask)=bitmask;
//no need for a result here, for binary that isn't checked (special case)
result:=true; //let the store result routine deal with it
end;
//byte:
function TScanner.ByteExact(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^=byte(value);
end;
function TScanner.ByteBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(pbyte(newvalue)^>=byte(value)) and (pbyte(newvalue)^<=byte(value2));
end;
function TScanner.ByteBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^>byte(value);
end;
function TScanner.ByteSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^<byte(value);
end;
function TScanner.ByteIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^>pbyte(oldvalue)^;
end;
function TScanner.ByteIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^=pbyte(oldvalue)^+byte(value);
end;
function TScanner.ByteIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^>trunc(pbyte(oldvalue)^+pbyte(oldvalue)^*svalue);
end;
function TScanner.ByteDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^<pbyte(oldvalue)^;
end;
function TScanner.ByteDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^=pbyte(oldvalue)^-byte(value);
end;
function TScanner.ByteDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^<trunc(pbyte(oldvalue)^-pbyte(oldvalue)^*svalue);
end;
function TScanner.ByteChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pbyte(newvalue)^<>pbyte(oldvalue)^;
end;
function TScanner.ByteUnchanged(newvalue,oldvalue: pointer):boolean; //also used for same as first
begin
result:=pbyte(newvalue)^=pbyte(oldvalue)^;
end;
//word:
function TScanner.WordExact(newvalue,oldvalue: pointer): boolean;
begin
result:=pword(newvalue)^=word(value);
end;
function TScanner.WordBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(pword(newvalue)^>=word(value)) and (pword(newvalue)^<=word(value2));
end;
function TScanner.WordBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^>word(value);
end;
function TScanner.WordSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^<word(value);
end;
function TScanner.WordIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^>pword(oldvalue)^;
end;
function TScanner.WordIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^=pword(oldvalue)^+word(value);
end;
function TScanner.WordIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^>trunc(pword(oldvalue)^+pword(oldvalue)^*svalue);
end;
function TScanner.WordDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^<pword(oldvalue)^;
end;
function TScanner.WordDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^=pword(oldvalue)^-word(value);
end;
function TScanner.WordDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^<trunc(pword(oldvalue)^-pword(oldvalue)^*svalue);
end;
function TScanner.WordChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^<>pword(oldvalue)^;
end;
function TScanner.WordUnchanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pword(newvalue)^=pword(oldvalue)^;
end;
//dword:
function TScanner.DWordExact(newvalue,oldvalue: pointer): boolean;
begin
result:=pdword(newvalue)^=dword(value);
end;
function TScanner.DWordBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(pdword(newvalue)^>=dword(value)) and (pdword(newvalue)^<=dword(value2));
end;
function TScanner.DWordBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^>dword(value);
end;
function TScanner.DWordSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^<dword(value);
end;
function TScanner.DWordIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^>pdword(oldvalue)^;
end;
function TScanner.DWordIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^=pdword(oldvalue)^+dword(value);
end;
function TScanner.DWordIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^>trunc(pdword(oldvalue)^+pdword(oldvalue)^*svalue);
end;
function TScanner.DWordDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^<pdword(oldvalue)^;
end;
function TScanner.DWordDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^=pdword(oldvalue)^-dword(value);
end;
function TScanner.DWordDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^<trunc(pdword(oldvalue)^-pdword(oldvalue)^*svalue);
end;
function TScanner.DWordChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^<>pdword(oldvalue)^;
end;
function TScanner.DWordUnchanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pdword(newvalue)^=pdword(oldvalue)^;
end;
//int64
function TScanner.QWordExact(newvalue,oldvalue: pointer): boolean;
begin
result:=puint64(newvalue)^=uint64(value);
end;
function TScanner.QWordBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(puint64(newvalue)^>=uint64(value)) and (puint64(newvalue)^<=uint64(value2));
end;
function TScanner.QWordBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^>uint64(value);
end;
function TScanner.QWordSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^<uint64(value);
end;
function TScanner.QWordIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^>puint64(oldvalue)^;
end;
function TScanner.QWordIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^=puint64(oldvalue)^+value;
end;
function TScanner.QWordIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^>trunc(puint64(oldvalue)^+puint64(oldvalue)^*svalue);
end;
function TScanner.QWordDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^<puint64(oldvalue)^;
end;
function TScanner.QWordDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^=puint64(oldvalue)^-value;
end;
function TScanner.QWordDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^<trunc(puint64(oldvalue)^-puint64(oldvalue)^*svalue);
end;
function TScanner.QWordChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^<>puint64(oldvalue)^;
end;
function TScanner.QWordUnchanged(newvalue,oldvalue: pointer):boolean;
begin
result:=puint64(newvalue)^=puint64(oldvalue)^;
end;
//single:
function TScanner.SingleExact(newvalue,oldvalue: pointer): boolean;
var min,max: single;
begin
result:=false;
case roundingtype of
rtRounded:
result:=(RoundTo(psingle(newvalue)^,-floataccuracy)=svalue);
rtExtremerounded:
result:=(psingle(newvalue)^>minsvalue) and (psingle(newvalue)^<maxsvalue);
rtTruncated:
result:=(psingle(newvalue)^>=svalue) and (psingle(newvalue)^<maxsvalue);
end;
end;
function TScanner.SingleBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(psingle(newvalue)^>=svalue) and (psingle(newvalue)^<=svalue2);
end;
function TScanner.SingleBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^>svalue;
end;
function TScanner.SingleSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^<svalue;
end;
function TScanner.SingleIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^>psingle(oldvalue)^;
end;
function TScanner.SingleIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=RoundTo(psingle(newvalue)^,-floataccuracy)=RoundTo(psingle(oldvalue)^+svalue,-floataccuracy);
end;
function TScanner.SingleIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^>psingle(oldvalue)^+psingle(oldvalue)^*svalue;
end;
function TScanner.SingleDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^<psingle(oldvalue)^;
end;
function TScanner.SingleDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=RoundTo(psingle(newvalue)^,-floataccuracy)=RoundTo(psingle(oldvalue)^-svalue,-floataccuracy);
end;
function TScanner.SingleDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=psingle(newvalue)^<psingle(oldvalue)^-psingle(oldvalue)^*svalue;
end;
function TScanner.SingleChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pSingle(newvalue)^<>pSingle(oldvalue)^;
end;
function TScanner.SingleUnchanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pSingle(newvalue)^=pSingle(oldvalue)^;
end;
//double:
function TScanner.DoubleExact(newvalue,oldvalue: pointer): boolean;
begin
result:=false;
case roundingtype of
rtRounded:
result:=(RoundTo(pdouble(newvalue)^,-floataccuracy)=dvalue);
rtExtremerounded:
result:=(pdouble(newvalue)^>mindvalue) and (pdouble(newvalue)^<maxdvalue);
rtTruncated:
result:=(pdouble(newvalue)^>=dvalue) and (pdouble(newvalue)^<maxdvalue);
end;
end;
function TScanner.DoubleBetween(newvalue,oldvalue: pointer):boolean;
begin
result:=(pdouble(newvalue)^>=dvalue) and (pdouble(newvalue)^<=dvalue2);
end;
function TScanner.DoubleBiggerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^>dvalue;
end;
function TScanner.DoubleSmallerThan(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^<dvalue;
end;
function TScanner.DoubleIncreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^>pdouble(oldvalue)^;
end;
function TScanner.DoubleIncreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=RoundTo(pdouble(newvalue)^,-floataccuracy)=RoundTo(pdouble(oldvalue)^+svalue,-floataccuracy);
end;
function TScanner.DoubleIncreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^>pdouble(oldvalue)^+pdouble(oldvalue)^*dvalue;
end;
function TScanner.DoubleDecreasedValue(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^<pdouble(oldvalue)^;
end;
function TScanner.DoubleDecreasedValueBy(newvalue,oldvalue: pointer):boolean;
begin
result:=RoundTo(pdouble(newvalue)^,-floataccuracy)=RoundTo(pdouble(oldvalue)^-svalue,-floataccuracy);
end;
function TScanner.DoubleDecreasedValueByPercentage(newvalue,oldvalue: pointer):boolean;
begin
result:=pdouble(newvalue)^<pdouble(oldvalue)^-pdouble(oldvalue)^*dvalue;
end;
function TScanner.DoubleChanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pDouble(newvalue)^<>pDouble(oldvalue)^;
end;
function TScanner.DoubleUnchanged(newvalue,oldvalue: pointer):boolean;
begin
result:=pDouble(newvalue)^=pDouble(oldvalue)^;
end;
//=============Save result routines===========//
procedure TScanner.GenericSaveResult(address: dword; oldvalue: pointer);
{
Generic routine for storing results. Use as last resort. E.g custom scans
}
begin
//save varsize
pdwordarray(CurrentAddressBuffer)[found]:=address;
copyMemory(pointer(dword(CurrentFoundBuffer)+(variablesize*found)),oldvalue,variablesize);
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.ByteSaveResult(address: dword; oldvalue: pointer);
begin
//save address and current value
pdwordarray(CurrentAddressBuffer)[found]:=address;
pbytearray(CurrentFoundBuffer)[found]:=pbyte(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.WordSaveResult(address: dword; oldvalue: pointer);
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
pwordarray(CurrentFoundBuffer)[found]:=pword(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.DWordSaveResult(address: dword; oldvalue: pointer);
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
pdwordarray(CurrentFoundBuffer)[found]:=pdword(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.QWordSaveResult(address: dword; oldvalue: pointer);
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
puint64array(CurrentFoundBuffer)[found]:=puint64(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.SingleSaveResult(address: dword; oldvalue: pointer);
begin
if not (isnan(psingle(oldvalue)^) or IsInfinite(psingle(oldvalue)^)) then
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
psinglearray(CurrentFoundBuffer)[found]:=psingle(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
end;
procedure TScanner.DoubleSaveResult(address: dword; oldvalue: pointer);
begin
if not (isnan(pdouble(oldvalue)^) or IsInfinite(pdouble(oldvalue)^)) then
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
pdoublearray(CurrentFoundBuffer)[found]:=pdouble(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
end;
procedure TScanner.arrayOfByteSaveResult(address: dword; oldvalue: pointer);
begin
pdwordarray(CurrentAddressBuffer)[found]:=address;
inc(found);
if found>=buffersize then
flushroutine;
end;
procedure TScanner.binarySaveResult(address: dword; oldvalue: pointer);
var i: integer;
begin
for i:=0 to 7 do
begin
if binaryresults[i] then
begin
PBitAddressArray(CurrentAddressBuffer)[found].address:=address;
PBitAddressArray(CurrentAddressBuffer)[found].bit:=i;
inc(found);
if found>=buffersize then
flushroutine;
end;
end;
end;
procedure TScanner.allSaveResult(address: dword; oldvalue: pointer);
{
note: eventually replace bit with a binary representation of all types that match
BUT, don't forget to change the foundlisthelper to handle this (since it'd be
multiple addresses in one entry, which isn't handled right now...
}
var i: TVariabletype;
begin
for i:=vtByte to vtDouble do
begin
if typesmatch[i] then
begin
if (i=vtsingle) then
begin
//filter out NAN and INF
if isnan(psingle(oldvalue)^) or IsInfinite(psingle(oldvalue)^) then
continue; //skip, don't save
end;
if (i=vtdouble) then
begin
if isnan(pdouble(oldvalue)^) or IsInfinite(pdouble(oldvalue)^) then
continue; //skip, don't save
end;
//using the bitaddressarray since it holds a address and a value big enough to hold all types
PBitAddressArray(CurrentAddressBuffer)[found].address:=address;
PBitAddressArray(CurrentAddressBuffer)[found].bit:=integer(i);
//save the current address, max variablesize is 8 bytes, so just store that
puint64array(CurrentFoundBuffer)[found]:=puint64(oldvalue)^;
inc(found);
if found>=buffersize then
flushroutine;
end;
end;
end;
//=============Flush result routines===========//
procedure TScanner.genericFlush;
var tempaddress,tempfound: pointer;
begin
scanwriter.writeresults(CurrentAddressBuffer,currentfoundbuffer,4*found,found*variablesize);
tempaddress:=CurrentAddressBuffer;
tempfound:=CurrentFoundBuffer;
CurrentAddressBuffer:=SecondaryAddressBuffer;
CurrentFoundBuffer:=SecondaryFoundBuffer;
SecondaryAddressBuffer:=tempaddress;
SecondaryFoundBuffer:=tempfound;
inc(totalfound,found);
found:=0;
end;
procedure TScanner.stringFlush;
begin
AddressFile.WriteBuffer(CurrentAddressBuffer^,4*found);
inc(totalfound,found);
found:=0;
end;
procedure TScanner.binaryFlush;
begin
AddressFile.WriteBuffer(CurrentAddressBuffer^,sizeof(TBitAddress)*found);
inc(totalfound,found);
found:=0;
end;
procedure TScanner.allFlush;
var tempaddress,tempfound: pointer;
begin
scanwriter.writeresults(CurrentAddressBuffer,currentfoundbuffer,sizeof(tbitaddress)*found,found*8);
tempaddress:=CurrentAddressBuffer;
tempfound:=CurrentFoundBuffer;
CurrentAddressBuffer:=SecondaryAddressBuffer;
CurrentFoundBuffer:=SecondaryFoundBuffer;
SecondaryAddressBuffer:=tempaddress;
SecondaryFoundBuffer:=tempfound;
inc(totalfound,found);
found:=0;
end;
//==================Tscanfilewriter=================//
procedure Tscanfilewriter.execute;
var part: integer;
begin
part:=0;
if writeError then exit;
try
repeat
dataavailable.WaitFor(infinite);
try
if terminated then exit;
scancontroller.resultsaveCS.Enter;
try
//note: Compressing before writing using TCompressStream=failure
//on fast compression speed it still took longer to compess and write
//than to only write the uncompressed buffer (no compress=2.8 secs,
//compressed=7.1 sec)
// cAddressFile.WriteBuffer(addressbuffer^,addressSize);
// cMemoryFile.WriteBuffer(memorybuffer^,memorySize);
part:=1;
AddressFile.WriteBuffer(addressbuffer^,addressSize);
part:=2;
MemoryFile.WriteBuffer(memorybuffer^,memorySize);
finally
scancontroller.resultsaveCS.Leave;
end;
finally
datawritten.SetEvent; //tell the others that you're ready to write again
end;
until terminated;
except
on e: exception do
begin
case part of
0: errorstring:='Thread synchronizer';
1: errorstring:=scanner.Addressfilename;
2: errorstring:=scanner.MemoryFilename;
else errorstring:='Unknown';
end;
errorstring:=errorstring+':'+e.message;
writeError:=true;
dataavailable.SetEvent;
datawritten.SetEvent;
end;
end;
end;
procedure Tscanfilewriter.writeresults(addressbuffer,memorybuffer: pointer; addressSize,memorySize: dword);
{
check if the thread is currently saving
If yes, wait, if not, start the thread, give it the buffer, and continue
}
begin
datawritten.WaitFor(infinite); //only gets set when the thread is done writing
//got past the wait, so it's done writing, so it has no need for the current variables
self.addressbuffer:=addressbuffer;
self.memorybuffer:=memorybuffer;
self.addressSize:=addressSize;
self.memorySize:=memorySize;
dataavailable.SetEvent; //tell the thread to start saving
//and return to the scanner, who should now swap scanbuffers
end;
procedure Tscanfilewriter.flush;
begin
if writeerror then exit;
datawritten.WaitFor(infinite);
datawritten.SetEvent;
end;
destructor Tscanfilewriter.destroy;
begin
if not terminated then
begin
terminate;
dataavailable.SetEvent;
waitfor;
end;
if datawritten<>nil then datawritten.Free;
if dataavailable<>nil then dataavailable.Free;
{
if cAddressFile<>nil then cAddressFile.free;
if cMemoryFile<>nil then cMemoryFile.free;
}
inherited destroy;
end;
constructor Tscanfilewriter.create(scanner: TScanner; scancontroller:TScanController; addressfile,memoryfile:TFileStream);
begin
self.scancontroller:=scancontroller;
self.scanner:=scanner;
self.addressfile:=addressfile;
self.memoryfile:=memoryfile;
//create the events
datawritten:=tevent.Create(nil,false,true,'');
dataavailable:=tevent.create(nil,false,false,'');
inherited create(false); //start it
end;
//=============TScanner===========//
procedure TScanner.FirstScanmem(base:dword; buffer: pointer; size: integer);
{
Scan the given buffer
Excludes the previousvalue buffer
}
var stepsize: integer;
lastmem: dword;
p: pbyte;
i: TVariableType;
_fastscan: boolean;
dividableby2: boolean;
dividableby4: boolean;
begin
_fastscan:=fastscan;
if _fastscan then
stepsize:=fastscanalignsize
else
stepsize:=1;
p:=buffer;
lastmem:=dword(p)+(size-variablesize); //optimizes compile to use reg if possible
if variableType=vtAll then
begin
//reset typesmatch array for each check
while (dword(p)<=lastmem) do
begin
if _fastscan then
begin
dividableby2:=dword(p) mod 2=0;
dividableby4:=dword(p) mod 4=0;
typesmatch[vtByte]:=true;
typesmatch[vtWord]:=dividableby2;
typesmatch[vtDWord]:=dividableby4;
typesmatch[vtQWord]:=dividableby4;
typesmatch[vtSingle]:=dividableby4;
typesmatch[vtDouble]:=dividableby4;
end
else
for i:=vtByte to vtDouble do typesmatch[i]:=true;
if checkroutine(p,nil) then //found one
StoreResultRoutine(base+dword(p)-dword(buffer),p);
inc(p,stepsize);
end;
end
else
begin
while (dword(p)<=lastmem) do
begin
if checkroutine(p,nil) then //found one
begin
StoreResultRoutine(base+dword(p)-dword(buffer),p);
if OnlyOne then
begin
AddressFound:=base+dword(p)-dword(buffer);
exit;
end;
end;
inc(p,stepsize);
end;
end;
end;
procedure TScanner.FirstNextScanmem(base:dword; buffer,oldbuffer: pointer; size: integer);
{
Scan the given buffer
}
var stepsize: integer;
lastmem: dword;
p,oldp: pbyte;
valuetype: TValueType;
_fastscan: boolean;
dividableby2: boolean;
dividableby4: boolean;
i: TVariableType;
begin
_fastscan:=fastscan;
if _fastscan then
stepsize:=fastscanalignsize
else
stepsize:=1;
p:=buffer;
oldp:=oldbuffer;
lastmem:=dword(p)+(size-variablesize); //optimizes compile to use reg if possible
{$ifndef standalonetrainerwithassemblerandaobscanner}
if scanOption=soSameAsFirst then //stupid, but ok...
begin
case self.variableType of
vtByte: valuetype:=vt_byte;
vtWord: valuetype:=vt_word;
vtDWord: valuetype:=vt_dword;
vtSingle: valuetype:=vt_single;
vtdouble: valuetype:=vt_double;
vtQword: valuetype:=vt_int64;
vtAll: valuetype:=vt_all;
end;
if valuetype=vt_all then
begin
while dword(p)<=lastmem do
begin
if _fastscan then
begin
dividableby2:=dword(p) mod 2=0;
dividableby4:=dword(p) mod 4=0;
typesmatch[vtByte]:=true;
typesmatch[vtWord]:=dividableby2;
typesmatch[vtDWord]:=dividableby4;
typesmatch[vtQWord]:=dividableby4;
typesmatch[vtSingle]:=dividableby4;
typesmatch[vtDouble]:=dividableby4;
end
else
for i:=vtByte to vtDouble do typesmatch[i]:=true;
if checkroutine(p,firstscanhandler.getpointertoaddress(base+dword(p)-dword(buffer),valuetype )) then //found one
StoreResultRoutine(base+dword(p)-dword(buffer),p);
inc(p, stepsize);
end;
end
else
begin
while dword(p)<=lastmem do
begin
if checkroutine(p,firstscanhandler.getpointertoaddress(base+dword(p)-dword(buffer),valuetype )) then //found one
StoreResultRoutine(base+dword(p)-dword(buffer),p);
inc(p, stepsize);
end;
end;
end
else
{$endif}
begin
if variableType=vtall then
begin
while dword(p)<=lastmem do
begin
if _fastscan then
begin
dividableby2:=dword(p) mod 2=0;
dividableby4:=dword(p) mod 4=0;
typesmatch[vtByte]:=true;
typesmatch[vtWord]:=dividableby2;
typesmatch[vtDWord]:=dividableby4;
typesmatch[vtQWord]:=dividableby4;
typesmatch[vtSingle]:=dividableby4;
typesmatch[vtDouble]:=dividableby4;
end
else
for i:=vtByte to vtDouble do typesmatch[i]:=true;
if checkroutine(p,oldp) then //found one
StoreResultRoutine(base+dword(p)-dword(buffer),p);
inc(p, stepsize);
inc(oldp, stepsize);
end;
end
else
begin
while dword(p)<=lastmem do
begin
if checkroutine(p,oldp) then //found one
StoreResultRoutine(base+dword(p)-dword(buffer),p);
inc(p, stepsize);
inc(oldp, stepsize);
end;
end;
end;
end;
procedure TScanner.nextnextscanmemAll(addresslist: pointer; oldmemory: pointer; chunksize: integer);
var stepsize: dword;
i,j,k: dword;
l: TVariableType;
maxindex: dword;
vsize: dword;
currentbase: dword;
newmemory: array [0..4095] of byte;
oldmem: pbytearray;
alist: pbitaddressarray;
actualread: dword;
so: Tscanoption;
valuetype: TValuetype;
currentaddress: dword;
begin
i:=0;
so:=scanoption;
maxindex:=chunksize;
vsize:=variablesize; //=8
oldmem:=oldmemory;
alist:=addresslist;
{$ifndef standalonetrainerwithassemblerandaobscanner}
valuetype:=vt_all;
{$endif}
while i<maxindex do
begin
j:=i+1;
currentbase:=alist[i].address and $FFFFF000;
while j<=maxindex do
begin
if (currentbase)=((alist[j].address+vsize-1) and $fffff000) then //same page
inc(j)
else
begin
dec(j); //now points to the last valid one, or the first one
break;
end;
end;
currentbase:=alist[i].address;
if readprocessmemory(processhandle,pointer(currentbase),@newmemory[0],(alist[j].address-currentbase)+vsize,actualread) then
begin
{$ifndef standalonetrainerwithassemblerandaobscanner}
if so=soSameAsFirst then
begin
//clear typesmatch and set current address
for l:=vtByte to vtDouble do
typesmatch[l]:=false;
currentaddress:=currentbase;
for k:=i to j do
begin
if alist[k].address=currentaddress then
begin
typesmatch[tvariabletype(alist[k].bit)]:=true;
end
else
begin
if checkroutine(@newmemory[currentaddress-currentbase],firstscanhandler.getpointertoaddress(currentaddress,valuetype )) then
StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]);
//clear typesmatch and set current address
for l:=vtByte to vtDouble do
typesmatch[l]:=false;
currentaddress:=alist[k].address;
typesmatch[tvariabletype(alist[k].bit)]:=true;
end;
end;
if checkroutine(@newmemory[currentaddress-currentbase],firstscanhandler.getpointertoaddress(currentaddress,valuetype )) then
StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]);
end
else
{$endif}
begin
//clear typesmatch and set current address
for l:=vtByte to vtDouble do
typesmatch[l]:=false;
currentaddress:=currentbase;
for k:=i to j do
begin
if alist[k].address=currentaddress then
begin
//add this one to the list
typesmatch[tvariabletype(alist[k].bit)]:=true;
continue;
end
else
begin
//we now have a list of entries with all the same address, k-1 points to the last one
if CheckRoutine(@newmemory[currentaddress-currentbase],@oldmem[(k-1)*vsize]) then
StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]);
//clear typesmatch and set current address
for l:=vtByte to vtDouble do
typesmatch[l]:=false;
currentaddress:=alist[k].address;
typesmatch[tvariabletype(alist[k].bit)]:=true;
end;
end;
if CheckRoutine(@newmemory[currentaddress-currentbase],@oldmem[j*vsize]) then
StoreResultRoutine(currentaddress,@newmemory[currentaddress-currentbase]);
end;
end;
i:=j+1;
end;
end;
procedure TScanner.nextnextscanmembinary(addresslist: pointer; chunksize: integer);
var stepsize: dword;
i,j,k,l: dword;
maxindex: dword;
vsize: dword;
currentbase: dword;
newmemory: array [0..4095] of byte;
oldmem: pbytearray;
alist: PBitAddressArray;
actualread: dword;
lastaddress: dword;
scannedbitlist: array [0..7] of boolean;
begin
i:=0;
maxindex:=chunksize;
vsize:=variablesize;
alist:=addresslist;
currentbase:=0;
while i<maxindex do
begin
j:=i+1;
currentbase:=alist[i].address and $FFFFF000;
while j<=maxindex do
begin
if (currentbase)=((alist[j].address+vsize-1) and $fffff000) then //same page
inc(j)
else
begin
dec(j); //now points to the last valid one, or the first one
break;
end;
end;
currentbase:=alist[i].address;
if readprocessmemory(processhandle,pointer(currentbase),@newmemory[0],(alist[j].address-currentbase)+vsize,actualread) then
begin
k:=i;
while k<=j do
begin
if CheckRoutine(@newmemory[alist[k].address-currentbase],nil) then
begin
for l:=0 to 7 do
scannedbitlist[l]:=false;
lastaddress:=alist[k].address;
while (alist[k].address=lastaddress) and (k<=j) do //nugfix might cause performance loss, check it later
begin
//add bits to the scanned bitlist
scannedbitlist[alist[k].bit]:=true;
inc(k);
end;
dec(k);
for l:=0 to 7 do
if not scannedbitlist[l] then binaryresults[l]:=false; //if it wasn't scanned, but the result was true, then set it to false
StoreResultRoutine(lastaddress,@newmemory[lastaddress-currentbase]);
end;
inc(k); //next
end;
end;
i:=j+1;
end;
end;
procedure TScanner.nextnextscanmem(addresslist: pointer; oldmemory: pointer; chunksize: integer);
{
gather addresses in a 4K region and readprocessmemory the difference between (so if only 2 addresses in a 4K block and address 1 is at 0 and address 2 at 2k, then only read 2k)
note: Do a test to see how many cpu cycles it costs to read 4K and 4bytes and find out the minimum number for mos toptimal speed
done: 4 byte scan takes an average of 4000 cycles (due to caches etc...)
4096 byte scan takes an average of 6500 cycles ...
2048 byte scan takes an average of 5900 cycles ...
conclusion: only when there is 1 address in the list, scan 1 address, else scan more
}
var stepsize: dword;
i,j,k: dword;
maxindex: dword;
vsize: dword;
currentbase: dword;
newmemory: array [0..4095] of byte;
oldmem: pbytearray;
alist: pdwordarray;
actualread: dword;
so: Tscanoption;
{$ifndef standalonetrainerwithassemblerandaobscanner}
valuetype: TValuetype;
{$endif}
currentaddress: dword;
begin
i:=0;
so:=scanoption;
maxindex:=chunksize;
vsize:=variablesize;
oldmem:=oldmemory;
alist:=addresslist;
{$ifndef standalonetrainerwithassemblerandaobscanner}
case variableType of
vtByte: valuetype:=vt_byte;
vtWord: valuetype:=vt_word;
vtDWord: valuetype:=vt_dword;
vtSingle: valuetype:=vt_single;
vtdouble: valuetype:=vt_double;
vtQword: valuetype:=vt_int64;
vtAll: valuetype:=vt_all;
vtCustom:
begin
case vsize of
1: valuetype:=vt_byte;
2: valuetype:=vt_word;
4: valuetype:=vt_dword;
8: valuetype:=vt_int64;
else valuetype:=vt_dword;
end;
end;
end;
{$endif}
while i<maxindex do
begin
j:=i+1;
currentbase:=alist[i] and $FFFFF000;
while j<=maxindex do
begin
if (currentbase)=((alist[j]+vsize-1) and $fffff000) then //same page
inc(j)
else
begin
dec(j); //now points to the last valid one, or the first one
break;
end;
end;
currentbase:=alist[i];
if readprocessmemory(processhandle,pointer(currentbase),@newmemory[0],(alist[j]-currentbase)+vsize,actualread) then
begin
{$ifndef standalonetrainerwithassemblerandaobscanner}
if so=soSameAsFirst then
begin
for k:=i to j do
if checkroutine(@newmemory[alist[k]-currentbase],firstscanhandler.getpointertoaddress(alist[k],valuetype )) then
StoreResultRoutine(alist[k],@newmemory[alist[k]-currentbase])
end
else
{$endif}
begin
for k:=i to j do
if CheckRoutine(@newmemory[alist[k]-currentbase],@oldmem[k*vsize]) then
StoreResultRoutine(alist[k],@newmemory[alist[k]-currentbase]);
end;
end;
i:=j+1;
end;
end;
procedure TScanner.configurescanroutine;
{
Parse scanvalue1 and scanvalue2 accordingly to the scanoptions and type
and fill in class variables that will be used for the scans
}
var FloatSettings: TFormatSettings;
BinaryString,andmask,bitmask: string;
i: integer;
b: Tbytes;
foundbuffersize: integer;
p: pointer;
begin
//fill FloatSettings with formatting data (e.g difference between , and . for decimal)
GetLocaleFormatSettings(GetThreadLocale, FloatSettings);
if scanOption in [soCustom, soExactValue,soValueBetween,soBiggerThan,soSmallerThan, soDecreasedValueBy, soIncreasedValueBy] then
begin
//user input is given
if scanvalue1='' then raise exception.Create('Please fill something in');
if variableType in [vtByte,vtWord,vtDWord,vtQword,vtAll,vtCustom] then
begin
//parse scanvalue1
try
if hexadecimal then
value:=strtoint64('$'+scanvalue1)
else
value:=strtoint64(scanvalue1);
except
if variableType=vtAll then
begin
try
dvalue:=strtofloat(scanvalue1,FloatSettings);
except
if FloatSettings.DecimalSeparator=',' then
FloatSettings.DecimalSeparator:='.'
else
FloatSettings.DecimalSeparator:=',';
//try again
try
dvalue:=strtofloat(scanvalue1,FloatSettings);
except
if variabletype<>vtCustom then
raise exception.Create(scanvalue1+' is not a valid value');
end;
end;
value:=trunc(dvalue);
end else
if variabletype<>vtCustom then
raise exception.Create(scanvalue1+' is an invalid value');
end;
if scanOption=soValueBetween then
begin
//also parse scanvalue2
try
if hexadecimal then
value2:=strtoint64('$'+scanvalue2)
else
value2:=strtoint64(scanvalue2);
except
if variableType=vtAll then
begin
try
dvalue:=strtofloat(scanvalue2,FloatSettings);
except
if FloatSettings.DecimalSeparator=',' then
FloatSettings.DecimalSeparator:='.'
else
FloatSettings.DecimalSeparator:=',';
//try again
try
dvalue:=strtofloat(scanvalue1,FloatSettings);
except
if variabletype<>vtCustom then
raise exception.Create(scanvalue1+' is not a valid value');
end;
end;
value:=trunc(dvalue);
end
else
if variabletype<>vtCustom then
raise exception.Create(scanvalue2+' is an invalid value');
end;
end;
end;
if percentage or (variableType in [vtsingle,vtDouble,vtAll,vtCustom]) then
begin
try
dvalue:=strtofloat(scanvalue1,FloatSettings);
except
if FloatSettings.DecimalSeparator=',' then
FloatSettings.DecimalSeparator:='.'
else
FloatSettings.DecimalSeparator:=',';
//try again
try
dvalue:=strtofloat(scanvalue1,FloatSettings);
except
if variabletype<>vtCustom then
raise exception.Create(scanvalue1+' is not a valid value');
end;
end;
if scanoption=soValueBetween then
begin
try
dvalue2:=strtofloat(scanvalue2,FloatSettings);
except
if FloatSettings.DecimalSeparator=',' then
FloatSettings.DecimalSeparator:='.'
else
FloatSettings.DecimalSeparator:=',';
//try again
try
dvalue2:=strtofloat(scanvalue1,FloatSettings);
except
if variabletype<>vtCustom then
raise exception.Create(scanvalue1+' is not a valid value');
end;
end;
end;
if percentage then
dvalue:=dvalue / 100;
svalue:=dvalue;
svalue2:=dvalue2;
if not percentage then //if it's not a percentage scan then round the userinput
begin
floataccuracy:=pos(FloatSettings.DecimalSeparator,scanvalue1);
if floataccuracy>0 then
floataccuracy:=length(scanvalue1)-floataccuracy;
svalue:=RoundTo(svalue,-floataccuracy);
svalue2:=RoundTo(svalue2,-floataccuracy);
dvalue:=RoundTo(dvalue,-floataccuracy);
dvalue2:=RoundTo(dvalue2,-floataccuracy);
end;
mindvalue:=dvalue-(1/(power(10,floataccuracy)));
maxdvalue:=dvalue+(1/(power(10,floataccuracy)));
minsvalue:=svalue-(1/(power(10,floataccuracy)));
maxsvalue:=svalue+(1/(power(10,floataccuracy)));
end;
if variableType = vtString then
begin
widescanvalue1:=scanvalue1;
end;
if variabletype = vtByteArray then
begin
ConvertStringToBytes(scanvalue1,hexadecimal,abs_arraytofind);
abs_arraylength:=length(abs_arraytofind);
end;
if variableType = vtBinary then
begin
if binaryStringAsDecimal then
begin
if hexadecimal then
begin
scanvalue1:=scanvalue1+'$';
scanvalue2:=scanvalue2+'$';
end;
binarystring:=inttobin(strtoint(scanvalue1))
end
else
binarystring:=scanvalue1;
andmask:='';
bitmask:='';
for i:=1 to length(binarystring) do
begin
if binarystring[i] in ['?','*'] then
begin
andmask:=andmask+'0';
bitmask:=bitmask+'0';
end
else
if binarystring[i] in ['0','1'] then
begin
andmask:=andmask+'1';
bitmask:=bitmask+binarystring[i];
end
else
if not (binarystring[i] in [' ',#8]) then
raise exception.Create(binarystring+' is not a valid notation');
end;
self.andmask:=BinToInt(andmask);
self.bitmask:=bintoint(bitmask);
end;
end;
FlushRoutine:=genericFlush; //change if not so
if variableType in [vtbinary,vtall] then
begin
getmem(CurrentAddressBuffer,buffersize*sizeof(Tbitaddress));
getmem(SecondaryAddressBuffer,buffersize*sizeof(Tbitaddress));
end
else
begin
getmem(CurrentAddressBuffer,buffersize*4);
getmem(SecondaryAddressBuffer,buffersize*4);
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
if scanOption=soSameAsFirst then //create a first scan handler
FirstScanHandler:=TFirstscanhandler.create;
{$endif}
case variableType of
vtByte:
begin
//byte config
FoundBufferSize:=buffersize*1;
StoreResultRoutine:=ByteSaveResult;
case scanOption of
soExactValue: checkRoutine:=byteExact;
soValueBetween: checkroutine:=byteBetween;
soBiggerThan: checkroutine:=byteBiggerThan;
soSmallerThan: checkroutine:=byteSmallerThan;
soIncreasedValue: checkroutine:=byteIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=byteIncreasedValueByPercentage
else
checkroutine:=byteIncreasedValueBy;
soDecreasedValue: checkroutine:=byteDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=byteDecreasedValueByPercentage
else
checkroutine:=byteDecreasedValueBy;
soChanged: checkroutine:=byteChanged;
soUnChanged: checkroutine:=byteUnchanged;
soSameAsFirst: checkroutine:=byteUnchanged;
end;
end;
vtWord:
begin
//word config
FoundBufferSize:=buffersize*2;
StoreResultRoutine:=WordSaveResult;
case scanOption of
soExactValue: checkRoutine:=wordExact;
soValueBetween: checkroutine:=wordBetween;
soBiggerThan: checkroutine:=wordBiggerThan;
soSmallerThan: checkroutine:=wordSmallerThan;
soIncreasedValue: checkroutine:=wordIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=wordIncreasedValueByPercentage
else
checkroutine:=wordIncreasedValueBy;
soDecreasedValue: checkroutine:=wordDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=wordDecreasedValueByPercentage
else
checkroutine:=wordDecreasedValueBy;
soChanged: checkroutine:=wordChanged;
soUnChanged: checkroutine:=wordUnchanged;
soSameAsFirst: checkroutine:=wordUnchanged;
end;
end;
vtDWord:
begin
//dword config
FoundBufferSize:=buffersize*4;
StoreResultRoutine:=DWordSaveResult;
case scanOption of
soExactValue: checkRoutine:=dwordExact;
soValueBetween: checkroutine:=dwordBetween;
soBiggerThan: checkroutine:=dwordBiggerThan;
soSmallerThan: checkroutine:=dwordSmallerThan;
soIncreasedValue: checkroutine:=dwordIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=dwordIncreasedValueByPercentage
else
checkroutine:=dwordIncreasedValueBy;
soDecreasedValue: checkroutine:=dwordDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=DWordDecreasedValueByPercentage
else
checkroutine:=dwordDecreasedValueBy;
soChanged: checkroutine:=dwordChanged;
soUnChanged: checkroutine:=dwordUnchanged;
soSameAsFirst: checkroutine:=dwordUnchanged;
end;
end;
vtQWord:
begin
//qword config
FoundBufferSize:=buffersize*8;
StoreResultRoutine:=QWordSaveResult;
case scanOption of
soExactValue: checkRoutine:=qwordExact;
soValueBetween: checkroutine:=qwordBetween;
soBiggerThan: checkroutine:=qwordBiggerThan;
soSmallerThan: checkroutine:=qwordSmallerThan;
soIncreasedValue: checkroutine:=qwordIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=qwordIncreasedValueByPercentage
else
checkroutine:=qwordIncreasedValueBy;
soDecreasedValue: checkroutine:=qwordDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=qwordDecreasedValueByPercentage
else
checkroutine:=qwordDecreasedValueBy;
soChanged: checkroutine:=qwordChanged;
soUnChanged: checkroutine:=qwordUnchanged;
soSameAsFirst: checkroutine:=qwordUnchanged;
end;
end;
vtSingle:
begin
//single config
FoundBufferSize:=buffersize*4;
StoreResultRoutine:=SingleSaveResult;
case scanOption of
soExactValue: checkRoutine:=singleExact;
soValueBetween: checkroutine:=singleBetween;
soBiggerThan: checkroutine:=singleBiggerThan;
soSmallerThan: checkroutine:=singleSmallerThan;
soIncreasedValue: checkroutine:=singleIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=singleIncreasedValueByPercentage
else
checkroutine:=singleIncreasedValueBy;
soDecreasedValue: checkroutine:=singleDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=singleDecreasedValueByPercentage
else
checkroutine:=singleDecreasedValueBy;
soChanged: checkroutine:=singleChanged;
soUnChanged: checkroutine:=singleUnchanged;
soSameAsFirst: checkroutine:=singleUnchanged;
end;
end;
vtDouble:
begin
//double config
FoundBufferSize:=buffersize*8;
StoreResultRoutine:=doubleSaveResult;
case scanOption of
soExactValue: checkRoutine:=doubleExact;
soValueBetween: checkroutine:=doubleBetween;
soBiggerThan: checkroutine:=doubleBiggerThan;
soSmallerThan: checkroutine:=doubleSmallerThan;
soIncreasedValue: checkroutine:=doubleIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=doubleIncreasedValueByPercentage
else
checkroutine:=doubleIncreasedValueBy;
soDecreasedValue: checkroutine:=doubleDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=doubleDecreasedValueByPercentage
else
checkroutine:=doubleDecreasedValueBy;
soChanged: checkroutine:=doubleChanged;
soUnChanged: checkroutine:=doubleUnchanged;
soSameAsFirst: checkroutine:=doubleUnchanged;
end;
end;
vtByteArray:
begin
CheckRoutine:=ArrayOfByteExact;
flushroutine:=stringFlush;
StoreResultRoutine:=ArrayOfByteSaveResult;
FoundBufferSize:=0;
end;
vtBinary:
begin
CheckRoutine:=BinaryExact;
StoreResultRoutine:=binarySaveResult;
flushroutine:=binaryFlush;
end;
vtString:
begin
//check if unicode or casesensitive is used
if not casesensitive then
begin
scanvalue1:=uppercase(scanvalue1);
widescanvalue1:=uppercase(widescanvalue1);
end;
flushroutine:=stringFlush;
case scanOption of
soExactValue:
begin
if casesensitive and unicode then CheckRoutine:=CaseSensitiveUnicodeStringExact;
if casesensitive and not unicode then CheckRoutine:=CaseSensitiveAnsiStringExact;
if not casesensitive and unicode then CheckRoutine:=CaseInsensitiveUnicodeStringExact;
if not casesensitive and not unicode then CheckRoutine:=CaseInsensitiveAnsiStringExact;
end;
end;
StoreResultRoutine:=arrayOfByteSaveResult; //arrayOfByteSaveResult is compatible since it saves only the address
end;
vtAll:
begin
//almost like binary
variablesize:=8; //override these variables
fastscanalignsize:=1;
FoundBufferSize:=buffersize*8;
StoreResultRoutine:=allSaveResult;
FlushRoutine:=allFlush;
case scanOption of
soExactValue: checkRoutine:=allExact;
soValueBetween: checkroutine:=allBetween;
soBiggerThan: checkroutine:=allBiggerThan;
soSmallerThan: checkroutine:=allSmallerThan;
soIncreasedValue: checkroutine:=allIncreasedValue;
soIncreasedValueBy: if percentage then
checkroutine:=allIncreasedValueByPercentage
else
checkroutine:=allIncreasedValueBy;
soDecreasedValue: checkroutine:=allDecreasedValue;
soDecreasedValueBy: if percentage then
checkroutine:=allDecreasedValueByPercentage
else
checkroutine:=allDecreasedValueBy;
soChanged: checkroutine:=allChanged;
soUnChanged: checkroutine:=allUnchanged;
soSameAsFirst: checkroutine:=allUnchanged;
end;
//the other types have to be filled in by the nextscan routines
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
vtCustom:
begin
case customscantype of
cstAutoAssembler:
begin
//execute autoassemblescript and read out the alloced addresses and vars
if not autoassemble(customscanscript,false,true,false,true,CustomScanAllocArray) then
raise exception.Create('Invalid Auto Assembler script');
for i:=0 to length(CustomScanAllocArray)-1 do
begin
if lowercase(CustomScanAllocArray[i].varname)='checkroutine' then
pointer(@checkroutine):=pointer(CustomScanAllocArray[i].address)
else
if lowercase(CustomScanAllocArray[i].varname)='prologue' then
customprologue:=pointer(CustomScanAllocArray[i].address)
else
if lowercase(CustomScanAllocArray[i].varname)='epilogue' then
customepilogue:=pointer(CustomScanAllocArray[i].address)
else
if lowercase(CustomScanAllocArray[i].varname)='fastscanstepsize' then
fastscanalignsize:=pdword(CustomScanAllocArray[i].address)^
else
if lowercase(CustomScanAllocArray[i].varname)='variablesize' then
begin
variablesize:=pdword(CustomScanAllocArray[i].address)^;
OwningScanController.OwningMemScan.customvariablesize:=variablesize;
end
else
if lowercase(CustomScanAllocArray[i].varname)='scantext' then
pdword(CustomScanAllocArray[i].address)^:=dword(@scanvalue1[1])
else
if lowercase(CustomScanAllocArray[i].varname)='scanvalue' then
puint64(CustomScanAllocArray[i].address)^:=value
else
if lowercase(CustomScanAllocArray[i].varname)='singlescanvalue' then
psingle(CustomScanAllocArray[i].address)^:=svalue
else
if lowercase(CustomScanAllocArray[i].varname)='doublescanvalue' then
pdouble(CustomScanAllocArray[i].address)^:=dvalue
else
if lowercase(CustomScanAllocArray[i].varname)='firstscan' then
if pinteger(CustomScanAllocArray[i].address)^=1 then
scanoption:=soSameAsFirst;
end;
//set the buffersizes to what they will be
scanOption:=soCustom;
FoundBufferSize:=buffersize*variablesize;
//use the generic save routine
StoreResultRoutine:=GenericSaveResult;
//and flush
FlushRoutine:=genericFlush;
//what better time to call customprologue then the present
customprologue();
end;
end;
end;
{$endif}
end;
getmem(CurrentFoundBuffer,FoundBufferSize);
getmem(SecondaryFoundBuffer,FoundBufferSize);
end;
procedure TScanner.nextNextscan;
var oldAddressfile: TFileStream;
oldMemoryfile: TFileStream;
i,j: integer;
stopindex: integer;
chunksize: integer;
oldaddresses: array of dword;
oldaddressesb: array of tbitaddress;
oldmemory: pointer;
part: integer;
begin
configurescanroutine;
oldAddressFile:=nil;
oldMemoryFile:=nil;
oldmemory:=nil;
try
oldAddressFile:=TFileStream.Create(CheatEngineDir+'Addresses.TMP',fmOpenRead or fmShareDenyNone);
oldMemoryFile:=TFileStream.Create(CheatEngineDir+'Memory.TMP',fmOpenRead or fmShareDenyNone);
//set the current index to startentry
stopindex:=stopentry-startentry; //going from 0 to stopindex
if self.variableType in [vtBinary,vtall] then
begin
//addressfile of tbitaddresstype
setlength(oldaddressesb,buffersize);
oldAddressFile.seek(7+sizeof(TBitAddress)*startentry,soFromBeginning);
if self.variableType=vtall then
begin
oldmemory:=virtualAlloc(nil,buffersize*variablesize,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE);
if oldmemory=nil then raise exception.Create('Error allocating '+inttostr(chunksize*variablesize)+' bytes for the old results. chunksize='+inttostr(chunksize)+' variablesize='+inttostr(variablesize));
end;
oldMemoryFile.seek(variablesize*startentry,soFromBeginning);
end
else
begin
setlength(oldaddresses,buffersize);
oldmemory:=virtualAlloc(nil,buffersize*variablesize,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE);
if oldmemory=nil then raise exception.Create('Error allocating '+inttostr(chunksize*variablesize)+' bytes for the old results. chunksize='+inttostr(chunksize)+' variablesize='+inttostr(variablesize));
oldAddressFile.seek(7+sizeof(DWORD)*startentry,soFromBeginning);
if not (self.variableType in [vtString,vtByteArray]) then
oldMemoryFile.seek(variablesize*startentry,soFromBeginning);
end;
//read in chunks of buffersize
i:=0;
while i<=stopindex do
begin
chunksize:=stopindex-i+1;
if chunksize>buffersize then
chunksize:=buffersize;
case variabletype of
vtBinary:
begin
oldAddressFile.ReadBuffer(oldaddressesb[0],chunksize*sizeof(tbitaddress));
nextnextscanmembinary(@oldaddressesb[0],chunksize);
end;
vtAll:
begin
oldAddressFile.ReadBuffer(oldaddressesb[0],chunksize*sizeof(tbitaddress));
if scanoption<>soSameAsFirst then
oldMemoryFile.ReadBuffer(oldmemory^,chunksize*variablesize);
nextnextscanmemall(@oldaddressesb[0],oldmemory,chunksize);
end;
else
begin
oldAddressFile.ReadBuffer(oldaddresses[0],chunksize*4);
if scanoption<>soSameAsFirst then
begin
if not (self.variableType in [vtString,vtByteArray]) then //skip the types with no previous result stored
oldMemoryFile.ReadBuffer(oldmemory^,chunksize*variablesize);
end;
nextnextscanmem(@oldaddresses[0],oldmemory,chunksize);
end;
end;
inc(scanned,chunksize);
inc(i,chunksize);
end;
flushroutine; //save all results temporarily stored in memory
finally
if oldAddressFile<>nil then oldAddressFile.free;
if oldMemoryFile<>nil then oldMemoryFile.free;
if oldmemory<>nil then virtualfree(oldmemory,0,MEM_RELEASE);
end;
end;
procedure TScanner.firstNextscan;
var
i: integer;
size: integer;
currentbase: dword;
startregion: integer;
stopregion: integer;
memorybuffer: ^byte;
oldbuffer: ^byte;
toread: integer;
actualread: dword;
hasleft: boolean;
begin
hasleft:=false;
startregion:=_startregion; //using a variable so stack can be used, with possibility of register
stopregion:=_stopregion;
//allocate a buffer for reading the new memory buffer
memorybuffer:=virtualAlloc(nil,maxregionsize+(variablesize-1),MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE);
try
//configure some variables and function pointers
configurescanroutine;
for i:=startregion to stopregion do
begin
if terminated or OwningScanController.Terminated then exit;
if i=startregion then
begin
currentbase:=startaddress;
toread:=OwningScanController.memregion[i].MemorySize-(startaddress-OwningScanController.memregion[i].BaseAddress);
//set oldbuffer and point to the exact start
oldbuffer:=OwningScanController.memregion[i].startaddress;
inc(oldbuffer, startaddress-OwningScanController.memregion[i].BaseAddress);
end
else
begin
currentbase:=OwningScanController.memregion[i].BaseAddress;
toread:=OwningScanController.memregion[i].MemorySize;
oldbuffer:=OwningScanController.memregion[i].startaddress;
end;
if (i=stopregion) and ((currentbase+toread)>stopaddress) then
toread:=stopaddress-currentbase;
//also try to read the last few bytes and add variablesize if needed
if (currentbase+toread)<(OwningScanController.memregion[i].BaseAddress+OwningScanController.memregion[i].MemorySize-variablesize) then
inc(toread, variablesize-1);
if toread>0 then //temp bugfix to find the real bug (what causes it?)
repeat
size:=toread;
if (size>buffersize) then size:=buffersize;
actualread:=0;
if size<toread then //there's something left to scan, so I can add the variablesize to it
ReadProcessMemory(processhandle,pointer(currentbase),memorybuffer,size+variablesize-1,actualread)
else
ReadProcessMemory(processhandle,pointer(currentbase),memorybuffer,size,actualread);
firstnextscanmem(currentbase,memorybuffer,oldbuffer,actualread);
inc(scanned,size); //for the progressbar
dec(toread,size);
inc(oldbuffer,size);
inc(currentbase,size);
until toread=0;
end;
flushroutine;
finally
virtualfree(memorybuffer,0,MEM_RELEASE);
end;
end;
procedure TScanner.firstscan;
var i,j: integer;
currentbase: dword;
size: dword;
actualread: dword;
memorybuffer: ^byte;
toread: dword;
startregion: integer;
stopregion: integer;
begin
//first find out where in the previousmemory of this thread starts
try
startregion:=_startregion; //using a variable so stack can be used, with possibility of register
stopregion:=_stopregion;
if scanOption<>soUnknownValue then
begin
//not unknown initial
memorybuffer:=virtualAlloc(nil,maxregionsize+16,MEM_COMMIT or MEM_TOP_DOWN , PAGE_READWRITE);
configurescanroutine;
end
else //it is a unknown initial value, so use the previousmemorybuffer instead
begin
memorybuffer:=pointer(dword(OwningScanController.OwningMemScan.previousMemoryBuffer)+dword(OwningScanController.memregion[startregion].startaddress)+(startaddress-OwningScanController.memregion[startregion].BaseAddress));
variablesize:=1; //ignore
end;
//now save the region between startaddress and stopaddress and create own memregion list
setlength(memregions,16);
for i:=startregion to stopregion do
begin
if terminated or OwningScanController.Terminated then exit;
if i=startregion then
begin
currentbase:=startaddress;
toread:=OwningScanController.memregion[i].MemorySize-(startaddress-OwningScanController.memregion[i].BaseAddress);
end
else
begin
currentbase:=OwningScanController.memregion[i].BaseAddress;
toread:=OwningScanController.memregion[i].MemorySize;
end;
if (i=stopregion) and ((currentbase+toread)>stopaddress) then
toread:=stopaddress-currentbase;
repeat
//05955958
size:=toread;
if (size>buffersize) then size:=buffersize;
actualread:=0;
//variablesize:=0;
if size<toread then
ReadProcessMemory(processhandle,pointer(currentbase),memorybuffer,size+variablesize-1,actualread)
else
ReadProcessMemory(processhandle,pointer(currentbase),memorybuffer,size,actualread);
//+variablesize for overlap, only when not unknown var
if scanOption=soUnknownValue then
begin
//unknown initial value, so create a memregion for this
memregions[memregionpos].BaseAddress:=currentbase;
memregions[memregionpos].MemorySize:=actualread;
memregions[memregionpos].startaddress:=memorybuffer;
inc(memorybuffer,size);
inc(memregionpos);
if (memregionpos mod 16) = 0 then
setlength(memregions,length(memregions)+16);
end
else
begin
//scan the buffer
firstscanmem(currentbase,memorybuffer,actualread);
end;
currentbase:=currentbase+size;
inc(scanned,size); //for the progressbar
dec(toread,size);
if (OnlyOne and (found>0)) then exit;
until terminated or (toread=0);
end;
if (scanOption<>soUnknownValue) then flushroutine; //save results
finally
if (scanOption<>soUnknownValue) and (memorybuffer<>nil) then
virtualfree(memorybuffer,0,MEM_RELEASE);
end;
end;
procedure TScanner.execute;
var i: integer;
begin
Set8087CW($133f); //disable floating point exceptions in this thread
{$ifdef crashtest}
//while (true) do
begin
sleep(16000);
end;
{$endif}
try
scanwriter:=TScanfilewriter.create(self,self.OwningScanController,addressfile,memoryfile);
if scantype=stFirstScan then firstscan;
if scantype=stNextScan then
begin
if useNextNextScan then
nextnextscan
else
firstnextscan;
end;
//tell scanwriter to stop
scanwriter.flush;
if scanwriter.writeError then
raise exception.Create('Disk write error:'+scanwriter.errorString);
except
on e: exception do
begin
haserror:=true;
errorstring:='thread '+inttostr(scannernr)+':'+e.message;
//tell all siblings to terminate, something messed up
//and I can just do this, since the ScanController is waiting for us, and terminate is pretty much atomic
//for i:=0 to length(OwningScanController.scanners)-1 do
OwningScanController.Terminate;
end;
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
if (variableType=vtCustom) and (length(CustomScanAllocArray)>0) then
begin
try
//custom scantype, and the script has been executed successfully (CustomScanAllocArray has been filled)
//call customepilogue
if assigned(customepilogue) then //customepilogue isn't nil so has been set
CustomEpilogue; //call it
//and dealloc the memory
autoassemble(customscanscript,false,false,false,true,CustomScanAllocArray);
except
on e: exception do
begin
if not haserror then
begin
haserror:=true;
errorstring:='Custom scan cleanup error:'+e.Message;
end else errorstring:=errorstring+#13#10+'Additional error:'+e.message;
end;
end;
end;
{$endif}
isdone:=true;
end;
destructor TScanner.destroy;
var i: integer;
begin
if AddressFile<>nil then //can be made nil by the scancontroller
begin
Addressfile.free;
DeleteFile(CheatEngineDir+'Addresses-'+inttostr(ThreadID)+'.TMP');
end;
if MemoryFile<>nil then
begin
MemoryFile.free;
DeleteFile(CheatEngineDir+'Memory-'+inttostr(ThreadID)+'.TMP');
end;
if scanwriter<>nil then
scanwriter.free;
if CurrentFoundBuffer<>nil then freemem(CurrentFoundBuffer);
if SecondaryFoundBuffer<>nil then freemem(SecondaryFoundBuffer);
if CurrentAddressBuffer<>nil then freemem(CurrentAddressBuffer);
if SecondaryAddressBuffer<>nil then freemem(SecondaryAddressBuffer);
{$ifndef standalonetrainerwithassemblerandaobscanner}
if firstscanhandler<>nil then firstscanhandler.free;
{$endif}
inherited destroy;
end;
constructor TScanner.create(suspended: boolean);
begin
inherited create(true); //do create the thread, I need the threadid
AddressFilename:=CheatEngineDir+'Addresses-'+inttostr(ThreadID)+'.TMP';
MemoryFilename:=CheatEngineDir+'Memory-'+inttostr(ThreadID)+'.TMP';
AddressFile:=TFileStream.Create(AddressFilename,fmCreate or fmSharedenynone);
MemoryFile:=TFileStream.Create(MemoryFilename,fmCreate or fmSharedenynone);
Priority:=cefuncproc.scanpriority;
if not suspended then resume; //would be stupid, but ok...
end;
//===============TScanController===============//
procedure TScanController.updategui;
var totaladdressestoscan, currentlyscanned: dword;
begin
//runs in mainthread
if OwningMemScan.progressbar<>nil then
begin
OwningMemScan.progressbar.Position:=OwningMemScan.GetProgress(totaladdressestoscan,currentlyscanned);
{$ifndef standalonetrainerwithassemblerandaobscanner}
SetProgressValue(OwningMemScan.progressbar.Position, OwningMemScan.progressbar.Max);
{$endif}
end;
end;
procedure TScanController.errorpopup;
begin
messagedlg(errorstring,mtError,[mbok],0);
end;
procedure TScanController.fillVariableAndFastScanAlignSize;
var s: string;
begin
case variableType of
vtByte:
begin
fastscanalignsize:=1;
variablesize:=1;
end;
vtWord:
begin
fastscanalignsize:=2;
variablesize:=2;
end;
vtDWord:
begin
fastscanalignsize:=4;
variablesize:=4;
end;
vtQWord:
begin
fastscanalignsize:=4;
variablesize:=8;
end;
vtSingle:
begin
fastscanalignsize:=4;
variablesize:=4;
end;
vtDouble:
begin
fastscanalignsize:=4;
variablesize:=8;
end;
vtByteArray:
begin
if scanoption<>soUnknownValue then
variablesize:=getBytecountArrayOfByteString(scanvalue1)
else
variablesize:=1;
fastscanalignsize:=1;
self.OwningMemScan.arrayLength:=variablesize;
end;
vtBinary:
begin
if scanoption<>soUnknownValue then
variablesize:=getBytecountBinaryString(scanvalue1,binaryStringAsDecimal)
else
variablesize:=1;
fastscanalignsize:=1;
//store some info for lookup
if binaryStringAsDecimal then //first convert do binarystring
s:=inttobin(strtoint(scanvalue1))
else
s:=trim(scanvalue1);
self.OwningMemScan.binaryLength:=length(s);
end;
vtString:
begin
if scanoption<>soUnknownValue then
begin
variablesize:=length(scanvalue1);
if unicode then variablesize:=variablesize*2;
end
else variablesize:=1;
fastscanalignsize:=1;
self.OwningMemScan.stringUnicode:=unicode;
self.OwningMemScan.stringLength:=length(scanvalue1);
end;
vtAll:
begin
variablesize:=8;
fastscanalignsize:=1;
end;
vtCustom:
begin
variablesize:=1; //customtypecallback(scanvalue1);
fastscanalignsize:=1;
end;
end;
OutputDebugString('fillVariableAndFastScanAlignSize:');
OutputDebugString(format('variableType=%d',[integer(variableType)]));
OutputDebugString(format('variablesize=%d',[variablesize]));
OutputDebugString(format('fastscanalignsize=%d',[fastscanalignsize]));
end;
procedure TScanController.NextNextScan;
{
NextNextScan will read results of the previous scan, and pass it off to scanner threads
}
var
AddressFile,MemoryFile: TFileStream;
blocksize: integer;
i: integer;
currententry: integer;
datatype: string[6];
begin
threadcount:=getcpucount;
//read the results and split up
AddressFile:=TFileStream.Create(CheatEngineDir+'Addresses.TMP',fmOpenRead or fmShareDenyNone);
try
if variableType in [vtbinary,vtall] then //it uses a specific TBitAddress instead of a dword
totalAddresses:=(addressfile.size-7) div 8
else
totalAddresses:=(addressfile.size-7) div 4;
//split up into blocks
//if totaladdresses>0 then
begin
blocksize:=totaladdresses div threadcount;
if (blocksize=0) and (totaladdresses>0) then blocksize:=1; //in case of 4 threads and totaladdresses is 3 or less
scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request
try
setlength(scanners,threadcount);
//setup the scanner threads
currententry:=0;
for i:=0 to threadcount-1 do
begin
scanners[i]:=tscanner.Create(true);
scanners[i].scannernr:=i;
scanners[i].OwningScanController:=self;
scanners[i].startentry:=currententry;
if i=threadcount-1 then
scanners[i].stopentry:=totalAddresses-1
else
scanners[i].stopentry:=currententry+blocksize;
if scanners[i].stopentry>=totaladdresses then
scanners[i].stopentry:=totalAddresses-1;
currententry:=scanners[i].stopentry+1; //next thread will start at the next one
scanners[i].scanType:=scanType; //stNextScan obviously
scanners[i].scanoption:=scanoption;
scanners[i].variableType:=VariableType;
scanners[i].roundingtype:=roundingtype;
scanners[i].percentage:=percentage;
scanners[i].fastscan:=fastscan;
scanners[i].scanValue1:=scanvalue1; //usual scanvalue
scanners[i].scanValue2:=scanValue2; //2nd value for between scan
scanners[i].readonly:=readonly;
scanners[i].unicode:=unicode;
scanners[i].caseSensitive:=caseSensitive;
scanners[i].hexadecimal:=hexadecimal;
scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal;
scanners[i].fastscanalignsize:=fastscanalignsize;
scanners[i].variablesize:=variablesize;
scanners[i].useNextNextscan:=true; //address result scan so nextnextscan
scanners[i].customscanscript:=customscanscript;
scanners[i].customscantype:=customscantype;
if i=0 then //first thread gets the header part
begin
datatype:='NORMAL';
scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype));
end;
end;
finally
scannersCS.leave;
end;
for i:=0 to length(scanners)-1 do
scanners[i].Resume;
OwningMemScan.found:=0;
//and now we wait
for i:=0 to threadcount-1 do
begin
while not (terminated or scanners[i].isdone) do
begin
WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
end;
//If terminated then stop the scanner thread and wait for it to finish
if terminated then
begin
scanners[i].Terminate;
scanners[i].WaitFor;
end;
if scanners[i].haserror then
begin
haserror:=true;
errorstring:=scanners[i].errorstring;
break;
end;
inc(OwningMemScan.found,scanners[i].totalfound);
end;
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
if haserror then
begin
//synchronize(errorpopup);
exit;
end;
end;
finally
addressfile.free; //release the file so it can be overwritten
end;
savescannerresults:=true;
end;
procedure TScanController.FirstNextScan;
{
FirstNextScan will read first scan regions, and pass it off to scanner threads
}
var
i,j: integer;
totalProcessMemorySize: dword;
blocksize: dword;
leftfromprevious: dword;
offsetincurrentregion: dword;
currentblocksize: dword;
memoryfile,addressfile: tfilestream;
datatype: string[6];
begin
threadcount:=GetCPUCount;
totalProcessMemorySize:=0;
memregion:=OwningMemscan.memRegion;
memregionpos:=OwningMemscan.memRegionPos;
{
read the addressfile and split it up into chunks for the scanner threads
important variables:
OwningMemScan.previousMemoryBuffer
OwningMemscan.memRegion
OwningMemscan.memRegionPos
}
//find total memoryammount
for i:=0 to memRegionPos do
inc(totalProcessMemorySize, memRegion[i].MemorySize);
totalAddresses:=totalProcessMemorySize;
//now split up into workloads
Blocksize:=totalProcessMemorySize div threadcount;
Blocksize:=blocksize-(blocksize mod 4096); //lastblock gets the missing bytes
//spawn the thread, but don't run till configured
scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request
try
setlength(scanners,threadcount);
j:=0; //start at memregion 0
leftfromprevious:=0;
offsetincurrentregion:=0;
for i:=0 to threadcount-1 do
begin
scanners[i]:=tscanner.Create(true);
scanners[i].scannernr:=i;
scanners[i].OwningScanController:=self;
scanners[i]._startregion:=j;
scanners[i].startaddress:=memRegion[j].BaseAddress+offsetincurrentregion;
scanners[i].maxregionsize:=0;
if i=(threadcount-1) then
begin
//this is the last scanner
scanners[i].stopaddress:=stopaddress; //let it go till the end
scanners[i]._stopregion:=memregionpos-1;
if scanOption<>soUnknownValue then
begin
//define maxregionsize
while j<memregionpos do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(j);
end;
end;
end
else
begin
currentblocksize:=0;
inc(currentblocksize,memregion[j].MemorySize-offsetincurrentregion);
inc(j);
scanners[i].maxregionsize:=currentblocksize;
//find the maximum blocksize
while (currentblocksize<blocksize) and (j<memregionpos) do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(currentblocksize,memregion[j].MemorySize);
inc(j);
end;
dec(j);
if scanners[i].maxregionsize=0 then //(currentblocksize<blocksize)
scanners[i].maxregionsize:=memregion[j].MemorySize;
scanners[i]._stopregion:=j;
scanners[i].stopaddress:=memregion[j].BaseAddress+memregion[j].MemorySize;
leftfromprevious:=currentblocksize-blocksize;
dec(scanners[i].stopaddress,leftfromprevious);
if leftfromprevious=0 then
begin
inc(j); //nothing left in this region
offsetincurrentregion:=0;
end else offsetincurrentregion:=memregion[j].MemorySize-leftfromprevious;
end;
if scanners[i].maxregionsize>buffersize then
scanners[i].maxregionsize:=buffersize;
//now configure the scanner thread with the same info this thread got, with some extra info
scanners[i].scanType:=scanType; //stNextScan obviously
scanners[i].scanoption:=scanoption;
scanners[i].variableType:=VariableType;
scanners[i].roundingtype:=roundingtype;
scanners[i].percentage:=percentage;
scanners[i].fastscan:=fastscan;
scanners[i].scanValue1:=scanvalue1; //usual scanvalue
scanners[i].scanValue2:=scanValue2; //2nd value for between scan
scanners[i].readonly:=readonly;
scanners[i].unicode:=unicode;
scanners[i].caseSensitive:=caseSensitive;
scanners[i].hexadecimal:=hexadecimal;
scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal;
scanners[i].fastscanalignsize:=fastscanalignsize;
scanners[i].variablesize:=variablesize;
scanners[i].useNextNextscan:=false; //region scan so firstnextscan
scanners[i].customscanscript:=customscanscript;
scanners[i].customscantype:=customscantype;
if i=0 then //first thread gets the header part
begin
datatype:='NORMAL';
scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype));
end;
end;
finally
scannersCS.Leave;
end;
//all threads are created, so start them
for i:=0 to length(scanners)-1 do
scanners[i].Resume;
savescannerresults:=true;
OwningMemScan.found:=0;
//and now we wait
for i:=0 to threadcount-1 do
begin
while not (terminated or scanners[i].isdone) do
begin
WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
end;
//If terminated then stop the scanner thread and wait for it to finish
if terminated then
begin
scanners[i].Terminate;
scanners[i].WaitFor;
end;
//scanners[i].WaitFor; //if the mainthread has to cancel, it has to tell the child scanners to terminate instead
if scanners[i].haserror then
begin
haserror:=true;
errorstring:=scanners[i].errorstring;
break;
end;
inc(OwningMemScan.found,scanners[i].totalfound);
end;
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
if haserror then
begin
//synchronize(errorpopup);
exit;
end;
//scan is successfull.
//now clean up some mem, it's not needed anymore
if OwningMemScan.previousMemoryBuffer<>nil then
begin
virtualfree(OwningMemScan.previousMemoryBuffer,0,MEM_RELEASE);
OwningMemscan.previousMemoryBuffer:=nil;
end;
end;
procedure TScanController.nextScan;
var AddressFile: TFilestream;
datatype: string[6];
begin
//open the address file and determine if it's a region scan or result scan
AddressFile:=TFileStream.Create(CheatEngineDir+'Addresses.TMP',fmOpenRead or fmSharedenynone);
try
Addressfile.ReadBuffer(datatype,sizeof(datatype));
finally
addressFile.free;
end;
if datatype='REGION' then
FirstNextScan
else
NextNextScan;
end;
procedure TScanController.firstScan;
{
first scan will gather the memory regions, open the files, and spawn scanners
}
var
currentBaseAddress: dword;
mbi : TMemoryBasicInformation;
i,j: integer;
Blocksize: dword;
currentblocksize: dword;
totalProcessMemorySize: dword;
leftfromprevious: dword;
offsetincurrentregion: dword;
datatype: string[6];
begin
OutputDebugString('TScanController.firstScan');
if OnlyOne then
threadcount:=1
else
threadcount:=GetCPUCount;
totalProcessMemorySize:=0;
OutputDebugString(format('threadcount=%d',[threadcount]));
{
ScanController plan:
spawn idle scanner threads , ammount=maxthreadcount in settings
enumerate all regions and split up into jobs for the threads
if scanoption=soUnknownValue make a buffer big enough to hold all the memory, and give the threads the startbase where in the buffer their first region will start
start scanner threads
}
//determine the size in bytes for this variable. If one is provided.
//also fill in the fastscan alignment as well
//else it's ignored and never used
setlength(memRegion,16);
memRegionPos:=0;
if OnlyOne then //don't go back, but forward
begin
if (startaddress mod 8)>0 then //align on a 8 byte base
startaddress:=startaddress-(startaddress mod 8)+8;
end
else
begin
if (startaddress mod 8)>0 then //align on a 8 byte base
startaddress:=startaddress-(startaddress mod 8);
end;
OutputDebugString(format('startaddress=%x',[startaddress]));
OutputDebugString('Finding out memory size');
currentBaseAddress:=startaddress;
while (Virtualqueryex(processhandle,pointer(currentBaseAddress),mbi,sizeof(mbi))<>0) and (currentBaseAddress<stopaddress) and ((currentBaseAddress+mbi.RegionSize)>currentBaseAddress) do //last check is done to see if it wasn't a 64-bit overflow.
begin
if (not (not scan_mem_private and (mbi.type_9=mem_private))) and (not (not scan_mem_image and (mbi.type_9=mem_image))) and (not (not scan_mem_mapped and (mbi.type_9=mem_mapped))) and (mbi.State=mem_commit) and ((mbi.Protect and page_guard)=0) and ((mbi.protect and page_noaccess)=0) then //look if it is commited
begin
if dword(mbi.BaseAddress)<startaddress then
begin
dec(mbi.RegionSize, startaddress-dword(mbi.BaseAddress));
mbi.BaseAddress:=pointer(startaddress);
end;
if dword(mbi.BaseAddress)+mbi.RegionSize>=stopaddress then
mbi.RegionSize:=stopaddress-dword(mbi.BaseAddress);
if //no cache check
(Skip_PAGE_NOCACHE and ((mbi.AllocationProtect and PAGE_NOCACHE)=PAGE_NOCACHE))
or
//no readonly check
((not readonly) and (not ((((mbi.AllocationProtect) and (page_readonly or page_execute_read))=0) and
(((mbi.Protect) and (page_readonly or PAGE_EXECUTE_READ))=0))))
then
begin
//skip it
currentBaseAddress:=dword(mbi.BaseAddress)+mbi.RegionSize;
continue;
end;
//still here, so valid
try
if memRegionPos>0 then
begin
//check if it can be appended to the previous region
if memRegion[memRegionPos-1].BaseAddress+memRegion[memRegionPos].MemorySize=dword(mbi.baseaddress) then //yes, append
begin
//yes, so append
memRegion[memRegionPos-1].MemorySize:=memRegion[memRegionPos-1].MemorySize+mbi.RegionSize;
continue;
end;
end;
//still here, so a new region
memRegion[memRegionPos].BaseAddress:=dword(mbi.baseaddress); //just remember this location
memRegion[memRegionPos].MemorySize:=mbi.RegionSize;
memRegion[memRegionPos].startaddress:=pointer(totalProcessMemorySize); //starts from 0, for unknown scans
inc(memRegionPos);
if (memRegionPos mod 16)=0 then //add another 16 to it
setlength(memRegion,length(memRegion)+16);
finally
inc(totalProcessMemorySize,mbi.RegionSize); //add this size to the total
end;
end;
currentBaseAddress:=dword(mbi.baseaddress)+mbi.RegionSize;
end;
OutputDebugString(format('memRegionPos=%d',[memRegionPos]));
for i:=0 to memRegionPos-1 do
OutputDebugString(format('i: %d B=%x S=%x SA=%p',[i, memRegion[i].BaseAddress, memRegion[i].MemorySize, memRegion[i].startaddress]));
OutputDebugString(format('totalProcessMemorySize=%x (%d)',[totalProcessMemorySize, totalProcessMemorySize]));
totalAddresses:=totalProcessMemorySize;
if memRegionPos=0 then raise exception.Create('No readable memory found');
//if soUnknown, make a buffer where it can store all the 'previous' memory
if scanOption=soUnknownValue then
begin
OutputDebugString('scanOption=soUnknownValue');
//extra check to make sure the previous scan was cleared
if OwningMemScan.previousMemoryBuffer<>nil then virtualfree(OwningMemScan.previousMemoryBuffer,0,MEM_RELEASE);
OutputDebugString(format('Allocating %xKB for previousMemoryBuffer',[totalProcessMemorySize div 1024]));
OwningMemScan.previousMemoryBuffer:=VirtualAlloc(nil,totalProcessMemorySize, MEM_COMMIT or MEM_TOP_DOWN, PAGE_READWRITE); //top down to try to prevent memory fragmentation
if OwningMemScan.previousMemoryBuffer=nil then
raise exception.Create('Failure allocating memory for copy');
OutputDebugString(format('Allocated at %p',[OwningMemScan.previousMemoryBuffer]));
end;
//split up into seperate workloads
OutputDebugString(format('Splitting up the workload between %d threads',[threadcount]));
Blocksize:=totalProcessMemorySize div threadcount;
if (Blocksize mod 4096) > 0 then
Blocksize:=blocksize-(blocksize mod 4096); //lastblock gets the missing bytes
OutputDebugString(format('Blocksize = %x',[Blocksize]));
scannersCS.Enter; //block access by the mainthread on the scanners object, could scanner[14] has not yet been created when doing a progress request
try
setlength(scanners,threadcount);
j:=0; //start at memregion 0
leftfromprevious:=0;
offsetincurrentregion:=0;
for i:=0 to threadcount-1 do
begin
OutputDebugString(format('Creating scanner %d',[i]));
scanners[i]:=tscanner.Create(true);
scanners[i].scannernr:=i;
scanners[i].OwningScanController:=self;
scanners[i]._startregion:=j;
scanners[i].startaddress:=memRegion[j].BaseAddress+offsetincurrentregion;
scanners[i].maxregionsize:=0;
if i=(threadcount-1) then
begin
//if it's the last thread, just give it what's left
scanners[i].stopaddress:=stopaddress;
scanners[i]._stopregion:=memregionpos-1;
//define maxregionsize
if scanOption<>soUnknownValue then
begin
//define maxregionsize , go from current till end (since it'll scan averything that's left)
while j<memregionpos do
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
inc(j);
end;
end;
end
else
begin
//not the last thread
currentblocksize:=0;
inc(currentblocksize,memregion[j].MemorySize-offsetincurrentregion);
inc(j);
scanners[i].maxregionsize:=currentblocksize;
while (currentblocksize<blocksize) and (j<memregionpos) do
begin
if scanOption<>soUnknownValue then //not a unknown initial value scan, so it doesn't need overlap
begin
if scanners[i].maxregionsize<memregion[j].MemorySize then
scanners[i].maxregionsize:=memregion[j].MemorySize;
end;
inc(currentblocksize,memregion[j].MemorySize);
inc(j);
end;
dec(j);
if scanners[i].maxregionsize=0 then //(currentblocksize<blocksize)
scanners[i].maxregionsize:=memregion[j].MemorySize;
scanners[i]._stopregion:=j;
scanners[i].stopaddress:=(memregion[j].BaseAddress+memregion[j].MemorySize);
//take off the bytes that are too many for this block
leftfromprevious:=currentblocksize-blocksize;
dec(scanners[i].stopaddress,leftfromprevious);
if leftfromprevious=0 then
begin
inc(j); //nothing left in this region
offsetincurrentregion:=0;
end else offsetincurrentregion:=memregion[j].MemorySize-leftfromprevious;
end;
OutputDebugString(format('startregion = %d',[scanners[i]._startregion]));
OutputDebugString(format('stopregion = %d',[scanners[i]._stopregion]));
OutputDebugString(format('startaddress = %x',[scanners[i].startaddress]));
OutputDebugString(format('stopaddress = %x',[scanners[i].stopaddress]));
OutputDebugString(format('j = %d',[j]));
OutputDebugString(format('leftfromprevious = %x',[leftfromprevious]));
OutputDebugString(format('offsetincurrentregion = %x',[offsetincurrentregion]));
if scanners[i].maxregionsize>buffersize then
scanners[i].maxregionsize:=buffersize;
OutputDebugString(format('maxregionsize = %x',[scanners[i].maxregionsize]));
//now configure the scanner thread with the same info this thread got, with some extra info
scanners[i].scanType:=scanType; //stFirstScan obviously
scanners[i].scanoption:=scanoption;
scanners[i].variableType:=VariableType;
scanners[i].roundingtype:=roundingtype;
scanners[i].percentage:=percentage;
scanners[i].fastscan:=fastscan;
scanners[i].scanValue1:=scanvalue1; //usual scanvalue
scanners[i].scanValue2:=scanValue2; //2nd value for between scan
scanners[i].readonly:=readonly;
scanners[i].unicode:=unicode;
scanners[i].OnlyOne:=OnlyOne;
scanners[i].caseSensitive:=caseSensitive;
scanners[i].hexadecimal:=hexadecimal;
scanners[i].binaryStringAsDecimal:=binaryStringAsDecimal;
scanners[i].fastscanalignsize:=fastscanalignsize;
scanners[i].variablesize:=variablesize;
scanners[i].customscanscript:=customscanscript;
scanners[i].customscantype:=customscantype;
if i=0 then //first thread gets the header part
begin
if scanoption=soUnknownValue then
datatype:='REGION'
else
datatype:='NORMAL';
scanners[i].AddressFile.WriteBuffer(datatype,sizeof(datatype));
end;
end;
finally
scannersCS.Leave;
end;
//all threads are created, so start them
for i:=0 to length(scanners)-1 do
scanners[i].Resume;
//prepare the result files
try
OwningMemScan.found:=0;
//and now we wait
for i:=0 to threadcount-1 do
begin
while not (terminated or scanners[i].isdone) do
begin
WaitForSingleObject(scanners[i].Handle,25); //25ms, an eternity for a cpu
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
end;
//If terminated then stop the scanner thread and wait for it to finish
if terminated then
begin
scanners[i].Terminate;
scanners[i].WaitFor;
end;
if scanners[i].haserror then
begin
OwningMemScan.found:=0;
haserror:=true;
errorstring:=scanners[i].errorstring;
break;
end;
inc(OwningMemScan.found,scanners[i].totalfound);
end;
if OnlyOne then
begin
if (scanners[0].found) or (scanners[0].totalfound)>0 then
begin
FoundSomething:=true;
AddressFound:=scanners[0].AddressFound;
end;
end;
if OwningMemScan.progressbar<>nil then
synchronize(updategui);
if haserror then
begin
OwningMemScan.found:=0;
//synchronize(errorpopup);
exit;
end;
//all threads are done
//combine all results and write them too the AddressFile and MemoryFile
if scanOption=soUnknownValue then
begin
//read the scanner memregions and adapt this memregion to it
//first find out how many regions it got
memRegionPos:=0;
for i:=0 to threadcount-1 do
inc(memRegionPos,scanners[i].memRegionPos);
setlength(OwningMemScan.memRegion,memRegionPos); //setting the size accordingly (max, can end up smaller due to appending)
OwningMemScan.memRegionPos:=0;
for i:=0 to threadcount-1 do
begin
for j:=0 to scanners[i].memRegionPos-1 do
begin
inc(OwningMemScan.found,scanners[i].memregions[j].MemorySize);
if OwningMemScan.memregionpos>0 then
begin
if OwningMemScan.memregion[OwningMemScan.memregionpos-1].BaseAddress+OwningMemScan.memregion[OwningMemScan.memregionpos-1].MemorySize=scanners[i].memregions[j].BaseAddress then
begin
//append
inc(OwningMemScan.memregion[OwningMemScan.memregionpos-1].MemorySize,scanners[i].memregions[j].MemorySize);
continue;
end;
end;
//new one
OwningMemScan.memregion[OwningMemScan.memregionpos]:=scanners[i].memregions[j];
inc(OwningMemScan.memregionpos);
end;
end;
if fastscan then //divide by alignsize of fastscan
OwningMemScan.found:=OwningMemScan.found div fastscanalignsize;
savescannerresults:=true;
end
else
begin
savescannerresults:=true;
end;
finally
end;
end;
procedure TScanController.execute;
var err: dword;
i: integer;
oldpos,oldmempos: integer;
begin
OutputDebugString('TScanController.execute');
try
//check what it is, and call first/next/nextnext- scan
err:=0;
errorstring:='';
try
fillVariableAndFastScanAlignSize;
if scantype=stFirstScan then firstscan;
if scantype=stNextScan then nextscan;
OutputDebugString('No exception on controller');
except
on e: exception do
begin
OutputDebugString(pchar('controller exception happened:'+e.message));
haserror:=true;
errorstring:='controller:'+e.message;
end;
end;
if OnlyOne then savescannerresults:=false; //DO NOT INTERFERE
if savescannerresults then //prepare saving. Set the filesize
begin
try
OutputDebugString('ScanController: creating undo files');
freeandnil(scanners[0].Addressfile);
freeandnil(scanners[0].Memoryfile);
//addresses
deletefile(CheatEngineDir+'Addresses.UNDO');
renamefile(CheatEngineDir+'Addresses.TMP',CheatEngineDir+'Addresses.UNDO');
renamefile(scanners[0].Addressfilename, CheatEngineDir+'Addresses.TMP');
//memory
deletefile(CheatEngineDir+'Memory.UNDO');
renamefile(CheatEngineDir+'Memory.TMP',CheatEngineDir+'Memory.UNDO');
renamefile(scanners[0].Memoryfilename, CheatEngineDir+'Memory.TMP');
try
AddressFile:=TFileStream.Create(CheatEngineDir+'Addresses.TMP',fmOpenWrite or fmShareDenyNone);
MemoryFile:=TFileStream.Create(CheatEngineDir+'Memory.TMP',fmOpenWrite or fmsharedenynone);
except
outputdebugstring('Scancontroller: Error when while loading result');
haserror:=true;
errorstring:='Error while loading results';
exit;
end;
oldpos:=addressfile.Size;
oldmempos:=memoryfile.size;
for i:=1 to threadcount-1 do
AddressFile.size:=AddressFile.size+scanners[i].Addressfile.Size;
outputdebugstring(format('ScanController: Have set AddressFile.size to %d',[AddressFile.size]));
except
on e: exception do
begin
OutputDebugString(pchar('Disk Write Error:'+e.message));
haserror:=true;
errorstring:='controller:Cleanup:ResultsPrepare:'+e.message;
end;
end;
end;
if haserror then
begin
err:=1;
outputdebugstring('There was an error during the scan:'+errorstring);
end;
//send message saying it's done
isdone:=true;
if notifywindow<>0 then
postMessage(notifywindow,notifymessage,err,0);
isdoneevent.setevent;
try
if savescannerresults and (addressfile<>nil) then //now actually save the scanner results
begin
//AddressFile should already have been created with the correct datatype and opened as denynone
AddressFile.Seek(oldpos,soFromBeginning);
Memoryfile.seek(oldmempos,soFromBeginning);
//save the exact results, and copy it to the AddressesFirst.tmp and Memoryfirst.tmp files
for i:=1 to length(scanners)-1 do
begin
outputdebugstring(format('ScanController: Writing results from scanner %d',[i]));
addressfile.CopyFrom(scanners[i].Addressfile,0);
Memoryfile.CopyFrom(scanners[i].MemoryFile,0);
end;
end;
except
on e: exception do
begin
OutputDebugString(pchar('Disk Write Error:'+e.message));
haserror:=true;
errorstring:='controller:Cleanup:ResultsWrite:'+e.message;
end;
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
try
if scantype=stFirstScan then
begin
outputdebugstring('ScanController: This was a first scan, so saving the First Scan results');
OwningMemScan.SaveFirstScanThread:=TSaveFirstScanThread.create(false,@OwningMemScan.memregion,@OwningMemScan.memregionpos, OwningMemScan.previousMemoryBuffer);
end;
except
on e: exception do
begin
OutputDebugString(pchar('First Scan Create:'+e.message));
haserror:=true;
errorstring:='controller:Cleanup:Failed spawning the Save First Scan thread:'+e.message;
end;
end;
{$endif}
//clean up secondary scanner threads, their destructor will close and delete their files
outputdebugstring('ScanController: Destroying scanner threads');
scannersCS.enter;
try
outputdebugstring('ScanController: Critical section "scannersCS" aquired');
for i:=0 to length(scanners)-1 do
begin
outputdebugstring(format('ScanController: Freeing scanner %d',[i]));
scanners[i].Free;
end;
setlength(scanners,0);
finally
scannersCS.leave;
outputdebugstring('ScanController: Critical section "scannersCS" released');
end;
//cleanup the files
if addressfile<>nil then addressfile.Free;
if MemoryFile<>nil then Memoryfile.Free;
except
on e: exception do
begin
OutputDebugString(pchar('controller exception happened:Unknown!'+e.message));
haserror:=true;
errorstring:='controller:Unknown!'+e.message;
end;
end;
end;
constructor TScanController.create(suspended: boolean);
begin
{$ifndef standalonetrainerwithassemblerandaobscanner}
SetProgressstate(tbpsNormal);
{$endif}
isdoneevent:=TEvent.create(nil,true,false,'');
scannersCS:=TCriticalSection.Create;
resultsaveCS:=TCriticalsection.create;
inherited create(suspended);
end;
destructor TScancontroller.destroy;
begin
scannersCS.free;
resultsaveCS.free;
inherited destroy;
end;
//----------------memscan--------------//
procedure TMemscan.TerminateScan(forceTermination: boolean);
var i: integer;
begin
if scancontroller<>nil then
begin
if not forceTermination then
begin
scanController.Terminate
end
else
begin
//scancontroller.forceTerminate:=true;
for i:=0 to length(scanController.scanners)-1 do
begin
try
TerminateThread(scanController.scanners[i].Handle,$dead);
scanController.scanners[i].Free;
except
end;
end;
setlength(scanController.scanners,0);
try
TerminateThread(scancontroller.Handle,$dead);
freeandnil(scancontroller);
except
end;
if notifywindow<>0 then
PostMessage(notifywindow, notifymessage,0,0);
scanController.isDoneEvent.SetEvent;
end;
end
else
raise exception.Create('For some reason the scanController does not exist');
//and now the caller has to wait
end;
function TMemscan.GetErrorString: string;
begin
result:='';
if scancontroller<>nil then
begin
scancontroller.WaitFor;
if scancontroller.haserror then
result:=scancontroller.errorstring;
end;
end;
procedure TMemscan.waittilldone;
begin
if scancontroller<>nil then
scancontroller.isdoneEvent.WaitFor(INFINITE);
end;
function TMemscan.GetOnlyOneResult(var address: dword):boolean;
begin
if self.scanController<>nil then
begin
result:=scancontroller.FoundSomething;
address:=scancontroller.AddressFound;
end;
end;
function TMemscan.GetProgress(var totaladdressestoscan:dword; var currentlyscanned: dword):integer;
{returns a value between 1 and 1000 representing how far the scan is}
var i: integer;
begin
//Take care of memory
if self.scanController<>nil then
begin
totaladdressestoscan:=self.scanController.totalAddresses;
currentlyscanned:=0;
scanController.scannersCS.enter;
try
for i:=0 to length(self.scanController.scanners)-1 do
inc(currentlyscanned,self.scanController.scanners[i].scanned);
finally
scanController.scannersCS.Leave;
end;
result:=trunc((currentlyscanned / totaladdressestoscan) * 1000);
end
else
begin
result:=0;
totaladdressestoscan:=0;
currentlyscanned:=0;
end;
end;
function TMemscan.GetFoundCount: uint64;
begin
result:=found;
end;
function TMemscan.Getcustomvariablesize: integer;
{
Rturns the veriable size of the custom type
only valid after having done a scan, not before
}
begin
result:=customvariablesize;
end;
function TMemscan.Getbinarysize: int64;
begin
case self.currentVariableType of
vtByte: result:=8;
vtWord: result:=16;
vtDWord: result:=32;
vtQWord: result:=64;
vtSingle: result:=32;
vtDouble: result:=64;
vtAll: result:=64;
vtString: if stringUnicode then result:=16*stringLength else result:=8*stringLength;
vtBinary: result:=binaryLength;
vtByteArray: result:=arrayLength*8;
else result:=8;
end;
end;
procedure TMemscan.newscan;
begin
if scanController<>nil then
begin
scanController.terminate;
scanController.WaitFor;
FreeAndNil(scanController);
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
if SaveFirstScanThread<>nil then
begin
SaveFirstScanThread.Terminate;
SaveFirstScanThread.WaitFor; //wait till it's done
freeandnil(SaveFirstScanThread);
end;
{$endif}
if previousMemoryBuffer<>nil then virtualfree(previousMemoryBuffer,0,MEM_RELEASE);
fLastscantype:=stNewScan;
end;
procedure TMemscan.NextScan(scanOption: TScanOption; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: dword; fastscan,readonly,hexadecimal,binaryStringAsDecimal, unicode, casesensitive, percentage: boolean; customscanscript: tstrings; customscantype: TCustomScanType);
begin
if scanController<>nil then
begin
scancontroller.WaitFor; //could be it's still saving the results of the previous scan
freeandnil(scanController);
end;
{$ifndef standalonetrainerwithassemblerandaobscanner}
if SaveFirstScanThread<>nil then
begin
SaveFirstScanThread.WaitFor; //wait till it's done
freeandnil(SaveFirstScanThread);
end;
{$endif}
scanController:=TscanController.Create(true);
scanController.OwningMemScan:=self;
scanController.scantype:=stNextScan;
scanController.scanOption:=scanOption;
scanController.variableType:=CurrentVariableType;
scanController.roundingtype:=roundingtype;
scanController.percentage:=percentage;
scanController.fastscan:=fastscan;
scanController.scanValue1:=scanvalue1; //usual scanvalue
scanController.scanValue2:=scanValue2; //2nd value for between scan
scanController.readonly:=readonly;
scanController.startaddress:=startaddress;
scanController.stopaddress:=stopaddress;
scancontroller.hexadecimal:=hexadecimal;
scancontroller.binaryStringAsDecimal:=binaryStringAsDecimal;
scancontroller.unicode:=unicode;
scancontroller.casesensitive:=casesensitive;
scancontroller.notifywindow:=notifywindow;
scancontroller.notifymessage:=notifymessage;
scancontroller.CustomScanScript:=CustomScanScript;
scanController.CustomScanType:=CustomScanType;
fLastscantype:=stNextScan;
scanController.Resume;
end;
procedure TMemscan.firstscan(scanOption: TScanOption; VariableType: TVariableType; roundingtype: TRoundingType; scanvalue1, scanvalue2: string; startaddress,stopaddress: dword; fastscan,readonly,hexadecimal,binaryStringAsDecimal,unicode,casesensitive,percentage: boolean; customscanscript: tstrings; customscantype: TCustomScanType);
{
Spawn the controller thread and fill it with the required data
Popup the wait window, or not ?
}
begin
if scanController<>nil then freeandnil(scanController);
{$ifndef standalonetrainerwithassemblerandaobscanner}
if SaveFirstScanThread<>nil then
begin
SaveFirstScanThread.Terminate; //it should quit, saving took to long and the user already started a new one
SaveFirstScanThread.WaitFor; //wait till it has fully terminated
freeandnil(SaveFirstScanThread);
end;
{$endif}
currentVariableType:=VariableType;
scanController:=TscanController.Create(true);
scanController.OwningMemScan:=self;
scanController.scantype:=stFirstScan;
scanController.scanOption:=scanOption;
scanController.variableType:=VariableType;
scanController.roundingtype:=roundingtype;
scanController.percentage:=percentage;
scanController.fastscan:=fastscan;
scanController.scanValue1:=scanvalue1; //usual scanvalue
scanController.scanValue2:=scanValue2; //2nd value for between scan
scanController.readonly:=readonly;
scanController.startaddress:=startaddress;
scanController.stopaddress:=stopaddress;
scancontroller.hexadecimal:=hexadecimal;
scancontroller.binaryStringAsDecimal:=binaryStringAsDecimal;
scancontroller.unicode:=unicode;
scancontroller.casesensitive:=casesensitive;
scancontroller.notifywindow:=notifywindow;
scancontroller.notifymessage:=notifymessage;
scancontroller.CustomScanScript:=CustomScanScript;
scanController.CustomScanType:=CustomScanType;
scanController.OnlyOne:=onlyone;
flastscantype:=stFirstScan;
scanController.Resume;
end;
procedure TMemscan.setScanDoneCallback(notifywindow: thandle; notifymessage: integer);
begin
self.notifywindow:=notifywindow;
self.notifymessage:=notifymessage;
end;
constructor TMemScan.create(progressbar: TProgressbar);
begin
self.progressbar:=progressbar;
customvariablesize:=1;
end;
destructor TMemScan.destroy;
begin
if previousMemoryBuffer<>nil then virtualfree(previousMemoryBuffer,0,MEM_RELEASE);
{$ifndef standalonetrainerwithassemblerandaobscanner}
if SaveFirstScanThread<>nil then SaveFirstScanThread.Free;
{$endif}
inherited Destroy;
end;
end.