mirror of
https://github.com/cheat-engine/cheat-engine
synced 2026-08-15 02:26:08 -04:00
1826 lines
49 KiB
ObjectPascal
Executable file
1826 lines
49 KiB
ObjectPascal
Executable file
unit d3dhookUnit;
|
|
{
|
|
This unit will inject the d3d hook dll into the target process and hook the
|
|
apropriate functions.
|
|
A shared object will be used for communicating states and data
|
|
}
|
|
|
|
{$mode delphi}
|
|
|
|
{$warn 5044 off}
|
|
|
|
interface
|
|
|
|
{$ifdef windows}
|
|
|
|
uses
|
|
windows, Classes, SysUtils, sharedMemory, forms, graphics, cefuncproc,
|
|
newkernelhandler, controls, Clipbrd, strutils, LuaHandler, RemoteMemoryManager,
|
|
math, syncobjs;
|
|
|
|
type
|
|
TCEMessage=packed record
|
|
uMsg: DWORD;
|
|
wParam: UINT64;
|
|
lParam: UINT64;
|
|
character: DWORD;
|
|
end;
|
|
|
|
TTextureEntry=packed record
|
|
AddressOfTexture: UINT64;
|
|
AddressOfFontmap: UINT64;
|
|
size: integer;
|
|
hasBeenUpdated: integer;
|
|
end;
|
|
|
|
TTextureEntryArray=Array [0..1000] of TTextureEntry;
|
|
PTextureEntryArray=^TTextureEntryArray;
|
|
|
|
type TRenderCommandEnum=(rcEndOfCommandlist=0, //Stop going through the list
|
|
rcIgnored=1, //Ignore (Being updated)
|
|
rcDrawSprite=2, //Render the sprite at the given position
|
|
rcDrawFont=3); //Render some text at the given coordinates. The string is located at "addressoftext"
|
|
|
|
|
|
TSpriteCommand=packed record
|
|
width: integer;
|
|
height: integer;
|
|
textureid: integer;
|
|
end;
|
|
|
|
TFontCommand=packed record
|
|
addressoftext: UINT64;
|
|
fontid: integer;
|
|
end;
|
|
|
|
TRenderCommand=packed record
|
|
command: integer;
|
|
x: single;
|
|
y: single;
|
|
alphablend: single;
|
|
|
|
centerX: single;
|
|
centerY: single;
|
|
rotation: single;
|
|
|
|
case TRenderCommandEnum of
|
|
rcDrawSprite: (Sprite: TSpriteCommand);
|
|
rcDrawFont: (Font: TFontCommand);
|
|
end;
|
|
PRenderCommand=^TRenderCommand;
|
|
|
|
TRenderCommandarray=array [0..100] of TRenderCommand;
|
|
PRenderCommandArray=^TRenderCommandArray;
|
|
|
|
|
|
TD3DHookShared=packed record
|
|
cheatenginedir: array [0..255] of char;
|
|
snapshotdir: array [0..255] of char;
|
|
dxgi_present: UINT64;
|
|
dxgi_resizebuffers: UINT64;
|
|
d3d9_present: UINT64;
|
|
d3d9_reset: UINT64;
|
|
|
|
d3d9_drawprimitive: UINT64;
|
|
d3d9_drawindexedprimitive: UINT64;
|
|
d3d9_drawprimitiveup: UINT64;
|
|
d3d9_drawindexedprimitiveup: UINT64;
|
|
d3d9_drawrectpatch: UINT64;
|
|
d3d9_drawtripatch: UINT64;
|
|
|
|
d3d10_drawindexed: UINT64;
|
|
d3d10_draw: UINT64;
|
|
d3d10_drawindexedinstanced: UINT64;
|
|
d3d10_drawinstanced: UINT64;
|
|
d3d10_drawauto: UINT64;
|
|
|
|
d3d11_drawindexed: UINT64;
|
|
d3d11_draw: UINT64;
|
|
d3d11_drawindexedinstanced: UINT64;
|
|
d3d11_drawinstanced: UINT64;
|
|
d3d11_drawauto: UINT64;
|
|
|
|
|
|
dxgi_newpresent: UINT64;
|
|
dxgi_newresizebuffers: UINT64;
|
|
d3d9_newpresent: UINT64;
|
|
d3d9_newreset: UINT64;
|
|
|
|
d3d9_newdrawprimitive: UINT64;
|
|
d3d9_newdrawindexedprimitive: UINT64;
|
|
d3d9_newdrawprimitiveup: UINT64;
|
|
d3d9_newdrawindexedprimitiveup: UINT64;
|
|
d3d9_newdrawrectpatch: UINT64;
|
|
d3d9_newdrawtripatch: UINT64;
|
|
|
|
d3d10_newdrawindexed: UINT64;
|
|
d3d10_newdraw: UINT64;
|
|
d3d10_newdrawindexedinstanced: UINT64;
|
|
d3d10_newdrawinstanced: UINT64;
|
|
d3d10_newdrawauto: UINT64;
|
|
|
|
d3d11_newdrawindexed: UINT64;
|
|
d3d11_newdraw: UINT64;
|
|
d3d11_newdrawindexedinstanced: UINT64;
|
|
d3d11_newdrawinstanced: UINT64;
|
|
d3d11_newdrawauto: UINT64;
|
|
|
|
|
|
dxgi_originalpresent: UINT64;
|
|
dxgi_originalresizebuffers: UINT64;
|
|
d3d9_originalpresent: UINT64;
|
|
d3d9_originalreset: UINT64;
|
|
|
|
d3d9_originaldrawprimitive: UINT64;
|
|
d3d9_originaldrawindexedprimitive: UINT64;
|
|
d3d9_originaldrawprimitiveup: UINT64;
|
|
d3d9_originaldrawindexedprimitiveup: UINT64;
|
|
d3d9_originaldrawrectpatch: UINT64;
|
|
d3d9_originaldrawtripatch: UINT64;
|
|
|
|
d3d10_originaldrawindexed: UINT64;
|
|
d3d10_originaldraw: UINT64;
|
|
d3d10_originaldrawindexedinstanced: UINT64;
|
|
d3d10_originaldrawinstanced: UINT64;
|
|
d3d10_originaldrawauto: UINT64;
|
|
|
|
d3d11_originaldrawindexed: UINT64;
|
|
d3d11_originaldraw: UINT64;
|
|
d3d11_originaldrawindexedinstanced: UINT64;
|
|
d3d11_originaldrawinstanced: UINT64;
|
|
d3d11_originaldrawauto: UINT64;
|
|
|
|
|
|
wireframe: integer;
|
|
disabledzbuffer: integer;
|
|
|
|
hookwnd: integer;
|
|
clipmouseinwindow: integer;
|
|
|
|
clickedoverlay: integer;
|
|
clickedx: integer;
|
|
clickedy: integer;
|
|
|
|
console: packed record
|
|
hasconsole: integer;
|
|
consolevisible: integer;
|
|
consolekey: dword;
|
|
overlayid: integer;
|
|
cursorid: integer;
|
|
lastmessage: TCEMessage;
|
|
|
|
end;
|
|
|
|
lastHwnd: DWORD;
|
|
texturelistHasUpdate: integer; //If 1 this means that CE might be waiting for the HasHandledTextureUpdate event (if it is a high priority update)
|
|
textureCount: integer;
|
|
texturelist: UINT64 ; //offset into texturelist based on the start of the shared object (once setup, this does not change)
|
|
TextureLock: UINT64 ; //target process handle
|
|
commandListLock: UINT64; //lockinforder: FIRST commandlist, THEN texturelist.
|
|
useCommandListLock: integer;
|
|
|
|
hasOnKey: integer;
|
|
|
|
snapshotKey: DWORD;
|
|
smallSnapshotKey: DWORD;
|
|
snapshotDone: UINT64; //Event to signal that a snapshot is done
|
|
snapshotImageFormat: integer;
|
|
snapshotcount: integer;
|
|
progressiveSnapshot: integer; //set to 1 if you do not wish the snapshot to clear the screen before each draw. (This makes it easier to see how a scene was build up)
|
|
alsoClearDepthBuffer: integer; //set to 1 if you also want the depth buffer to be cleared before each draw
|
|
savePNGSeperateAsWell: integer;
|
|
canDoSnapshot: integer;
|
|
initialized: DWORD;
|
|
|
|
|
|
|
|
//followed by the rendercommands
|
|
|
|
|
|
end;
|
|
PD3DHookShared=^TD3DHookShared;
|
|
|
|
|
|
|
|
type
|
|
TD3DHook_RenderObject=class;
|
|
TD3DClickEvent=procedure(renderobject: TObject; x,y: integer) of object;
|
|
TD3DKeyDownEvent=function(VirtualKey: dword; char: pchar): boolean of object;
|
|
|
|
TD3DHook=class;
|
|
TD3DHook_Texture=class;
|
|
TD3DHook_Sprite=class;
|
|
TD3DHook_Fontmap=class;
|
|
TD3DHook_TextContainer=class;
|
|
|
|
TD3DMessageHandler=class(tthread)
|
|
private
|
|
owner: TD3DHook;
|
|
rendercommandid, x,y: integer; //clicked overlay
|
|
|
|
lastmessage: TCEMessage;
|
|
|
|
|
|
|
|
console: record
|
|
log: Tstringlist;
|
|
cursorpos: integer;
|
|
|
|
backgroundtexture: TD3DHook_Texture;
|
|
background: TD3DHook_Sprite;
|
|
cursortexture: TD3DHook_Texture;
|
|
cursor: TD3DHook_sprite;
|
|
seperator: TD3dhook_sprite; //horizontal line splitting commandline from output
|
|
fontmap: TD3DHook_Fontmap;
|
|
output: TD3DHook_textcontainer; //history/output
|
|
commandline: TD3DHook_TextContainer; //commandline
|
|
end;
|
|
procedure setConsoleCursorPos;
|
|
|
|
|
|
procedure doclick;
|
|
procedure dokeyboard;
|
|
procedure handleSnapshot;
|
|
public
|
|
procedure execute; override;
|
|
end;
|
|
|
|
|
|
TD3DHook_Texture=class(TObject)
|
|
private
|
|
resource: ptruint; //address where the current resource is located
|
|
protected
|
|
id: integer;
|
|
owner: TD3DHook;
|
|
fheight: integer;
|
|
fwidth: integer;
|
|
public
|
|
function getID: integer;
|
|
{
|
|
|
|
procedure LoadTextureFromFile(filename: string);
|
|
|
|
constructor Create(filename: string); overload; }
|
|
procedure LoadTextureByPicture(picture: TPicture);
|
|
constructor Create(owner: TD3DHook; picture: TPicture);
|
|
constructor Create(owner: TD3DHook);
|
|
destructor destroy; override;
|
|
published
|
|
property Height: integer read fheight;
|
|
property Width: integer read fwidth;
|
|
end;
|
|
|
|
TD3DHook_FontMap=class(TD3DHook_Texture)
|
|
private
|
|
fontmapdata: ptruint;
|
|
localfontmapcopy: TMemorystream; //used by calculateFontwidth
|
|
public
|
|
function calculateFontWidth(s:string): integer;
|
|
procedure ChangeFont(font: TFont);
|
|
constructor Create(owner: TD3DHook; font: TFont);
|
|
destructor destroy; override;
|
|
end;
|
|
|
|
TD3DHook_RenderObject=class(TObject)
|
|
private
|
|
fx: single;
|
|
fy: single;
|
|
fcenterX: single;
|
|
fcenterY: single;
|
|
fRotation: single; //stored as deg here, internally uses rad
|
|
falphablend: single;
|
|
fvisible: boolean;
|
|
procedure setRotation(v: single);
|
|
procedure setCenterX(v: single);
|
|
procedure setCenterY(v: single);
|
|
procedure setX(v: single);
|
|
procedure setY(v: single);
|
|
procedure setAlphaBlend(v: single);
|
|
procedure setVisible(state: boolean);
|
|
procedure setZOrder(newpos: integer);
|
|
protected
|
|
owner: TD3DHook;
|
|
updatecount: integer;
|
|
public
|
|
function getIndex: integer;
|
|
procedure beginUpdate;
|
|
procedure endUpdate;
|
|
procedure UpdateRenderCommand; virtual; abstract;
|
|
|
|
destructor destroy; override;
|
|
constructor create(owner: TD3DHook);
|
|
|
|
published
|
|
property ZOrder: integer read getIndex write setZOrder;
|
|
property Alphablend: single read falphablend write setAlphablend;
|
|
property X: single read fx write setX;
|
|
property Y: single read fy write setY;
|
|
property CenterX: single read fcenterx write setCenterX;
|
|
property CenterY: single read fcentery write setCenterY;
|
|
property Rotation: single read frotation write setRotation;
|
|
property Visible: boolean read fVisible write setVisible;
|
|
end;
|
|
|
|
TD3Dhook_TextContainer=class(TD3DHook_RenderObject) //class for rendering text
|
|
private
|
|
maxTextLength: integer;
|
|
AddressOfText: QWORD;
|
|
fFontMap: TD3DHook_FontMap;
|
|
fText: string;
|
|
|
|
addressChangedThisUpdate: boolean;
|
|
oldAddressOfText: QWORD;
|
|
private
|
|
procedure setFontMap(fm: TD3DHook_FontMap);
|
|
procedure setText(s: string);
|
|
public
|
|
procedure UpdateRenderCommand; override;
|
|
|
|
|
|
destructor destroy; override;
|
|
constructor create(owner: TD3DHook; fontmap: TD3DHook_FontMap; x,y: single; text: string; minsize: integer=0);
|
|
published
|
|
property FontMap: TD3DHook_FontMap read fFontMap write setFontMap;
|
|
property Text: String read fText write setText;
|
|
end;
|
|
|
|
TD3DHook_Sprite=class(TD3DHook_RenderObject) //clas for rendering textures
|
|
private
|
|
ftexture: TD3DHook_Texture;
|
|
fwidth: integer;
|
|
fheight: integer;
|
|
|
|
|
|
procedure setTexture(s: TD3DHook_Texture);
|
|
|
|
procedure setWidth(v: integer);
|
|
procedure setHeight(v: integer);
|
|
|
|
public
|
|
|
|
procedure UpdateRenderCommand; override;
|
|
|
|
constructor create(owner: TD3DHook; texture: TD3DHook_Texture);
|
|
published
|
|
property Width: integer read fWidth write setWidth;
|
|
property Height: integer read fHeight write setHeight;
|
|
property Texture: TD3DHook_Texture read ftexture write setTexture;
|
|
end;
|
|
|
|
|
|
TD3DHook=class(TObject)
|
|
private
|
|
hooked: boolean;
|
|
fonKeyDown: TD3DKeyDownEvent;
|
|
fonclick: TD3DClickEvent;
|
|
sharename: string;
|
|
shared: PD3DHookShared; //local address of the D3DHookShared structure
|
|
|
|
tea: PTextureEntryArray;
|
|
|
|
|
|
fmhandle: THandle;
|
|
|
|
textures: TList;
|
|
commandlist: TList; //collection of pointer to objects used for lookup and id management. 1-on-1 relation to the renderCommands list
|
|
renderCommandList: PRenderCommandArray;
|
|
|
|
isupdating: integer; //update counter for the texture list
|
|
isupdatingCL: integer; //update counter for the command list
|
|
|
|
maxsize: integer;
|
|
fprocessid: dword;
|
|
|
|
hasclickevent: THandle;
|
|
hashandledclickevent: THandle;
|
|
|
|
haskeyboardevent: THandle; //todo: combine into one "HasMessage" event, but for now, to make sure I don't break anything, this method...
|
|
hashandledkeyboardevent: THandle;
|
|
|
|
SnapshotDone: THandle;
|
|
texturelock: THandle;
|
|
CommandListLock: THandle;
|
|
|
|
messagehandler: TD3DMessageHandler;
|
|
|
|
|
|
|
|
memman: TRemoteMemoryManager;
|
|
|
|
commandlistCS: TCriticalSection;
|
|
|
|
procedure setOnKeyDown(s: TD3DKeyDownEvent);
|
|
public
|
|
|
|
|
|
procedure beginTextureUpdate;
|
|
procedure endTextureUpdate;
|
|
|
|
procedure beginCommandListUpdate;
|
|
procedure endCommandListUpdate;
|
|
|
|
function createTexture(f: string): TD3DHook_Texture; overload;
|
|
function createTexture(p: TPicture): TD3DHook_Texture; overload;
|
|
function createSprite(texture: TD3DHook_Texture): TD3DHook_Sprite;
|
|
|
|
function createFontMap(f: TFont): TD3DHook_FontMap;
|
|
function createTextContainer(fontmap: TD3DHook_FontMap; x,y: single; text: string): TD3Dhook_TextContainer;
|
|
|
|
function getDisabledZBuffer: boolean;
|
|
procedure setDisabledZBuffer(state: boolean);
|
|
function getWireframeMode: boolean;
|
|
procedure setWireframeMode(state: boolean);
|
|
function getMouseClip: boolean;
|
|
procedure setMouseClip(state: boolean);
|
|
|
|
function getWidth: integer;
|
|
function getHeight: integer;
|
|
|
|
procedure setCommandListLockFeature(state: boolean);
|
|
|
|
|
|
procedure enableConsole(virtualkey: DWORD);
|
|
|
|
procedure setSnapshotOptions(path: string; full, small: dword; Progressive: boolean; cleardepthbuffer: boolean; savepng: boolean; pictureFormat: integer);
|
|
|
|
|
|
|
|
constructor create(size: integer; hookhwnd: boolean=true);
|
|
destructor destroy; override;
|
|
published
|
|
property Width: integer read getWidth;
|
|
property Height: integer read getHeight;
|
|
property DisabledZBuffer: boolean read GetDisabledZBuffer write setDisabledZBuffer;
|
|
property WireframeMode: boolean read GetWireframeMode write setWireframeMode;
|
|
property MouseClip: boolean read GetMouseclip write setMouseClip;
|
|
property Processid: dword read fprocessid;
|
|
property OnKeyDown: TD3DKeyDownEvent read fonKeyDown write setOnKeyDown;
|
|
property OnClick: TD3DClickEvent read fonclick write fonclick;
|
|
end;
|
|
|
|
|
|
|
|
|
|
var D3DHook: TD3DHook;
|
|
|
|
function safed3dhook(size: integer=16*1024*1024; hookwindow: boolean=true): TD3DHook;
|
|
procedure FixAlpha(aPNG: TPortableNetworkGraphic);
|
|
|
|
{$endif}
|
|
|
|
implementation
|
|
|
|
{$ifdef windows}
|
|
uses frmautoinjectunit, autoassembler, MainUnit, frmSaveSnapshotsUnit,
|
|
frmsnapshothandlerUnit, symbolhandler, ProcessHandlerUnit, Globals;
|
|
|
|
resourcestring
|
|
rsTheD3dhookObjectHasNotBeenCreatedYet = 'The d3dhook object has not been created yet';
|
|
rsD3DHookFailureToOpenTheSharedMemoryObject = 'D3DHook: Failure to open the shared memory object';
|
|
rsD3DHookFailureToMapTheSharedMemoryObject = 'D3DHook: Failure to map the shared memory object';
|
|
|
|
procedure TD3DMessageHandler.handleSnapshot;
|
|
begin
|
|
if frmSaveSnapshots=nil then
|
|
frmSaveSnapshots:=TfrmSaveSnapshots.create(application);
|
|
|
|
frmSaveSnapshots.btnCombinedSelect.visible:=owner.shared.progressiveSnapshot=0;
|
|
|
|
frmSaveSnapshots.initialize(owner.shared.snapshotdir, owner.shared.snapshotcount);
|
|
|
|
frmSaveSnapshots.showmodal;
|
|
owner.shared.canDoSnapshot:=1;
|
|
|
|
|
|
if frmSaveSnapshots.saved.Count>0 then
|
|
begin
|
|
if frmsnapshothandler=nil then
|
|
frmsnapshothandler:=TfrmSnapshotHandler.create(application);
|
|
|
|
frmsnapshothandler.show;
|
|
frmsnapshothandler.loadsnapshots(frmSaveSnapshots.saved);
|
|
end;
|
|
|
|
//exit the function and wait for a new event
|
|
end;
|
|
|
|
procedure TD3DMessageHandler.setConsoleCursorPos;
|
|
var s: string;
|
|
begin
|
|
if console.cursor<>nil then
|
|
begin
|
|
console.cursor.y:=console.commandline.y;
|
|
|
|
s:=copy(console.commandline.Text,1, console.cursorpos);
|
|
|
|
console.cursor.x:=console.fontmap.calculateFontWidth(s);
|
|
end;
|
|
end;
|
|
|
|
procedure TD3DMessageHandler.dokeyboard;
|
|
var virtualkey: dword;
|
|
scancode: dword;
|
|
|
|
c,c2: string;
|
|
s: pchar;
|
|
old: tstrings;
|
|
p: tpicture;
|
|
f: tfont;
|
|
maxlines: integer;
|
|
currentlines: integer;
|
|
i: integer;
|
|
l: string;
|
|
begin
|
|
//check the size of the window. If it's changed, reinitialize the consoleoverlay
|
|
|
|
owner.shared.console.lastmessage.uMsg:=0; //don't handle the keypress in the target process
|
|
|
|
|
|
if assigned(owner.onkeydown) then
|
|
begin
|
|
if owner.onkeydown(lastmessage.wParam, pchar(@lastmessage.character)) then
|
|
owner.shared.console.lastmessage.uMsg:=1; //if it returns true, let it go through
|
|
|
|
end;
|
|
|
|
if (owner.shared.console.hasconsole=0) or (owner.shared.console.consolevisible=0) then exit;
|
|
|
|
//just create or show the console
|
|
if console.background=nil then
|
|
begin
|
|
owner.beginTextureUpdate;
|
|
|
|
try
|
|
p:=tpicture.Create;
|
|
p.png.canvas.brush.color:=clBlack;
|
|
p.png.width:=1;
|
|
p.png.height:=1;
|
|
|
|
console.backgroundtexture:=TD3DHook_Texture.Create(owner, p);
|
|
p.free;
|
|
|
|
console.background:=TD3DHook_Sprite.create(owner, console.backgroundtexture);
|
|
|
|
|
|
console.background.width:=-1; //full width
|
|
console.background.height:=owner.getHeight div 2;
|
|
console.background.x:=0;
|
|
console.background.y:=owner.getHeight div 2;
|
|
console.background.alphaBlend:=0.80;
|
|
|
|
p:=tpicture.Create;
|
|
p.png.canvas.brush.color:=clWhite;
|
|
p.png.width:=1;
|
|
p.png.height:=1;
|
|
p.png.Canvas.Pixels[0,0]:=clWhite;
|
|
console.cursortexture:=TD3DHook_Texture.Create(owner, p);
|
|
p.free;
|
|
|
|
console.cursor:=TD3DHook_sprite.create(owner, console.cursortexture);
|
|
|
|
|
|
f:=tfont.create;
|
|
f.Assign(mainform.Font);
|
|
f.color:=clWhite;
|
|
f.Size:=f.size*2;
|
|
|
|
console.fontmap:=TD3DHook_FontMap.Create(owner, f);
|
|
console.commandline:=TD3Dhook_TextContainer.create(owner, console.fontmap, 0, owner.getHeight-console.fontmap.height,'',256);
|
|
|
|
console.seperator:=TD3DHook_sprite.create(owner, console.cursortexture); //same color
|
|
console.seperator.x:=0;
|
|
console.seperator.y:=owner.getHeight-console.fontmap.height;
|
|
console.seperator.height:=1;
|
|
console.seperator.width:=-1; //full width
|
|
|
|
|
|
console.output:=TD3Dhook_TextContainer.create(owner, console.fontmap, 0, owner.getHeight div 2,'',8192);
|
|
|
|
|
|
console.cursor.width:=3;
|
|
console.cursor.height:=console.fontmap.height;
|
|
//
|
|
finally
|
|
owner.endTextureUpdate;
|
|
end;
|
|
end;
|
|
|
|
|
|
setConsoleCursorPos;
|
|
|
|
if lastmessage.uMsg=$ffffffff then
|
|
begin
|
|
console.background.visible:=true;
|
|
console.seperator.visible:=true;
|
|
console.cursor.visible:=true;
|
|
console.output.visible:=true;
|
|
console.commandline.visible:=true;
|
|
exit; //nothing else to do
|
|
end;
|
|
|
|
if lastmessage.uMsg=$fffffffe then
|
|
begin
|
|
console.background.visible:=false;
|
|
console.seperator.visible:=false;
|
|
console.cursor.visible:=false;
|
|
console.output.visible:=false;
|
|
console.commandline.visible:=false;
|
|
exit;
|
|
end;
|
|
|
|
|
|
//handle the key
|
|
virtualkey:=lastmessage.wParam;
|
|
scancode:=(lastmessage.lparam shr 16) and $FF;
|
|
|
|
owner.beginTextureUpdate;
|
|
|
|
//handle keys like delete, backspace, etc...
|
|
case virtualkey of
|
|
VK_RETURN:
|
|
begin
|
|
//execute the command
|
|
console.log.add(console.commandline.Text);
|
|
|
|
old:=lua_oldprintoutput;
|
|
lua_setPrintOutput(console.log);
|
|
lua_dostring(LuaVM, pchar(console.commandline.Text));
|
|
lua_setPrintOutput(old);
|
|
|
|
//calculate the max number of lines
|
|
maxlines:=trunc((console.seperator.y-console.background.y) / console.output.FontMap.height);
|
|
currentlines:=0;
|
|
l:='';
|
|
for i:=max(0, console.log.Count-maxlines) to console.log.Count-1 do
|
|
begin
|
|
l:=l+console.log[i]+#13#10;
|
|
|
|
inc(currentlines);
|
|
if (currentlines>=maxlines) then break;
|
|
end;
|
|
|
|
|
|
console.output.y:=console.seperator.y-(currentlines*console.output.FontMap.height);
|
|
console.output.Text:=l;
|
|
|
|
console.commandline.Text:='';
|
|
console.cursorpos:=0;
|
|
end;
|
|
VK_LEFT:
|
|
begin
|
|
if console.cursorpos>0 then
|
|
dec(console.cursorpos);
|
|
end;
|
|
|
|
VK_RIGHT:
|
|
begin
|
|
if console.cursorpos<length(console.commandline.Text) then
|
|
inc(console.cursorpos);
|
|
end;
|
|
VK_BACK:
|
|
begin
|
|
//delete the character before the cursorpos
|
|
if console.cursorpos>0 then
|
|
begin
|
|
c:=copy(console.commandline.text, 1, console.cursorpos-1);
|
|
c2:=copy(console.commandline.text, console.cursorpos+1, length(console.commandline.Text));
|
|
console.commandline.text:=c+c2;
|
|
dec(console.cursorpos);
|
|
end;
|
|
end;
|
|
|
|
VK_DELETE:
|
|
begin
|
|
//delete the character after the current cursor
|
|
c:=copy(console.commandline.text, 1, console.cursorpos);
|
|
c2:=copy(console.commandline.text, console.cursorpos+2, length(console.commandline.text));
|
|
console.commandline.text:=c+c2;
|
|
end
|
|
else
|
|
begin
|
|
//not a control key, check if it's a character
|
|
if lastmessage.character<>0 then
|
|
begin
|
|
//it's a character
|
|
s:=@lastmessage.character;
|
|
|
|
c:=copy(console.commandline.text, 1, console.cursorpos);
|
|
c2:=copy(console.commandline.text, console.cursorpos+1, length(console.commandline.text));
|
|
|
|
|
|
console.commandline.text:=c+s+c2;
|
|
|
|
inc(console.cursorpos);
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
setConsoleCursorPos;
|
|
|
|
owner.endTextureUpdate;
|
|
|
|
end;
|
|
|
|
procedure TD3DMessageHandler.doclick;
|
|
begin
|
|
if assigned(owner.onclick) then
|
|
owner.onclick(TD3DHook_RenderObject(owner.commandlist[rendercommandid]), x,y);
|
|
end;
|
|
|
|
procedure TD3DMessageHandler.execute;
|
|
var eventlist: array of THandle;
|
|
r: dword;
|
|
|
|
cursor: boolean;
|
|
cursorstart: dword;
|
|
begin
|
|
cursorstart:=gettickcount;
|
|
console.log:=tstringlist.create;
|
|
console.cursorpos:=0;
|
|
|
|
setlength(eventlist,3);
|
|
eventlist[0]:=owner.hasclickevent;
|
|
eventlist[1]:=owner.haskeyboardevent;
|
|
eventlist[2]:=owner.SnapshotDone;
|
|
|
|
while (not terminated) do
|
|
begin
|
|
r:=WaitForMultipleObjects(3, @eventlist[0], false, 100);
|
|
case r of
|
|
WAIT_OBJECT_0:
|
|
begin
|
|
//click event: quickly save the variables and tell the game to continue
|
|
x:=owner.shared.clickedx;
|
|
y:=owner.shared.clickedy;
|
|
rendercommandid:=owner.shared.clickedoverlay;
|
|
SetEvent(owner.hashandledclickevent);
|
|
|
|
Synchronize(doclick);
|
|
end;
|
|
|
|
WAIT_OBJECT_0+1:
|
|
begin
|
|
//keyboard event
|
|
lastmessage:=owner.shared.console.lastmessage;
|
|
Synchronize(dokeyboard);
|
|
SetEvent(owner.hashandledkeyboardevent);
|
|
cursorstart:=GetTickCount;
|
|
end;
|
|
|
|
WAIT_OBJECT_0+2:
|
|
begin
|
|
//snapshot event
|
|
synchronize(handlesnapshot);
|
|
end;
|
|
end;
|
|
|
|
if (owner.shared.console.consolevisible=1) and (console.cursor<>nil) then //toggle the cursor visible or invisible based on the current time and if a key was pressed (keep the cursor visible rigth after pressing a key, so reset the timerstart)
|
|
console.cursor.visible:=(((GetTickCount-cursorstart) mod 1000)<500);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
//----------------------------D3DHook_RenderObject------------------------------
|
|
procedure TD3DHook_RenderObject.setRotation(v: single);
|
|
begin
|
|
if fRotation=v then exit;
|
|
|
|
beginUpdate;
|
|
fRotation:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setCenterX(v: single);
|
|
begin
|
|
if fcenterX=v then exit;
|
|
|
|
beginUpdate;
|
|
fcenterX:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setCenterY(v: single);
|
|
begin
|
|
if fcenterY=v then exit;
|
|
|
|
beginUpdate;
|
|
fcenterY:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setAlphaBlend(v: single);
|
|
begin
|
|
if falphablend=v then exit;
|
|
|
|
BeginUpdate;
|
|
falphablend:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setX(v: single);
|
|
begin
|
|
if fx=v then exit;
|
|
|
|
beginUpdate;
|
|
fx:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setY(v: single);
|
|
begin
|
|
if fy=v then exit;
|
|
|
|
beginUpdate;
|
|
fy:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
|
|
procedure TD3DHook_RenderObject.setVisible(state: boolean);
|
|
begin
|
|
if fvisible=state then exit; //no change, no update
|
|
|
|
beginUpdate;
|
|
fvisible:=state;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.setZOrder(newpos: integer);
|
|
var
|
|
mypos: integer;
|
|
replaced: TD3DHook_RenderObject;
|
|
begin
|
|
//change the order of the commandlist
|
|
mypos:=zorder;
|
|
if (newpos<>mypos) and (newpos<owner.commandlist.count) then
|
|
begin
|
|
replaced:=TD3DHook_RenderObject(owner.commandlist[newpos]);
|
|
|
|
owner.beginCommandListUpdate;
|
|
|
|
owner.renderCommandList^[newpos].command:=integer(rcIgnored); //in case the locking option is not used. Will cause some flickering in the worst case
|
|
owner.renderCommandList^[mypos].command:=integer(rcIgnored);
|
|
|
|
owner.commandlist[newpos]:=self;
|
|
owner.commandlist[mypos]:=replaced;
|
|
|
|
//now update mine and the replaced one's rendercommand entry
|
|
UpdateRenderCommand;
|
|
if replaced<>nil then
|
|
replaced.UpdateRenderCommand;
|
|
|
|
owner.endCommandListUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TD3DHook_RenderObject.getIndex: integer;
|
|
begin
|
|
result:=owner.commandlist.IndexOf(self);
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.beginUpdate;
|
|
begin
|
|
inc(updatecount);
|
|
end;
|
|
|
|
procedure TD3DHook_RenderObject.endUpdate;
|
|
begin
|
|
if updatecount>0 then
|
|
dec(updatecount);
|
|
|
|
if updatecount=0 then
|
|
UpdateRenderCommand;
|
|
end;
|
|
|
|
destructor TD3DHook_RenderObject.destroy;
|
|
var index: integer;
|
|
begin
|
|
index:=getindex;
|
|
owner.renderCommandList^[Index].command:=integer(rcIgnored);
|
|
owner.commandlist[index]:=nil;
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
constructor TD3DHook_RenderObject.create(owner: TD3DHook);
|
|
begin
|
|
self.owner:=owner;
|
|
falphablend:=1;
|
|
fvisible:=true;
|
|
owner.commandlist.Add(self);
|
|
end;
|
|
|
|
//----------------------------D3DHook_TextContainer-----------------------------
|
|
procedure TD3Dhook_TextContainer.setText(s: string);
|
|
var
|
|
x: ptruint;
|
|
nul: byte;
|
|
begin
|
|
BeginUpdate;
|
|
|
|
fText:=s;
|
|
if length(s)+1>maxTextLength then //+1 for 0 terminator
|
|
begin
|
|
if AddressOfText<>0 then //free the old block
|
|
owner.memman.dealloc(AddressOfText);
|
|
|
|
maxTextLength:=length(s)+16;
|
|
AddressOfText:=owner.memman.alloc(maxTextLength);
|
|
end;
|
|
|
|
if s='' then
|
|
begin
|
|
nul:=0;
|
|
WriteProcessMemory(processhandle, pointer(AddressOfText), @nul, 1, x);
|
|
end
|
|
else
|
|
WriteProcessMemory(processhandle, pointer(AddressOfText), @s[1], length(s)+1, x); //just write
|
|
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3Dhook_TextContainer.setFontMap(fm: TD3DHook_FontMap);
|
|
begin
|
|
BeginUpdate;
|
|
fFontMap:=fm;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3Dhook_TextContainer.UpdateRenderCommand;
|
|
var index: integer;
|
|
begin
|
|
index:=getindex;
|
|
owner.beginCommandListUpdate;
|
|
|
|
if (index<>-1) and (fFontmap<>nil) and (fVisible) then
|
|
begin
|
|
if owner.renderCommandList^[index].command<>integer(rcDrawFont) then //something completely new
|
|
owner.renderCommandList^[index].command:=integer(rcIgnored);
|
|
|
|
owner.renderCommandList^[index].x:=x;
|
|
owner.renderCommandList^[index].y:=y;
|
|
owner.renderCommandList^[index].rotation:=degtorad(Rotation);
|
|
owner.renderCommandList^[index].centerx:=centerx;
|
|
owner.renderCommandList^[index].centery:=centery;
|
|
owner.renderCommandList^[index].alphablend:=alphablend;
|
|
owner.renderCommandList^[index].Font.fontid:=fFontmap.getID;
|
|
owner.renderCommandList^[index].Font.addressoftext:=AddressOfText;
|
|
|
|
owner.renderCommandList^[index].command:=integer(rcDrawFont);
|
|
end
|
|
else
|
|
owner.renderCommandList^[index].command:=integer(rcIgnored);
|
|
|
|
owner.endCommandListUpdate;
|
|
end;
|
|
|
|
destructor TD3Dhook_TextContainer.destroy;
|
|
begin
|
|
if AddressOfText<>0 then
|
|
owner.memman.dealloc(addressoftext);
|
|
inherited destroy;
|
|
end;
|
|
|
|
constructor TD3Dhook_TextContainer.create(owner: TD3DHook; fontmap: TD3DHook_FontMap; x,y: single; text: string; minsize: integer=0);
|
|
begin
|
|
inherited create(owner);
|
|
|
|
beginupdate;
|
|
|
|
if minsize<>0 then
|
|
begin
|
|
maxTextLength:=minsize;
|
|
AddressOfText:=owner.memman.alloc(maxTextLength);
|
|
end;
|
|
|
|
self.x:=x;
|
|
self.y:=y;
|
|
self.FontMap:=fontmap;
|
|
self.text:=text;
|
|
|
|
endUpdate;
|
|
end;
|
|
|
|
//-------------------------------D3DHook_Sprite---------------------------------
|
|
procedure TD3DHook_Sprite.setWidth(v: integer);
|
|
begin
|
|
beginUpdate;
|
|
fwidth:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_Sprite.setHeight(v: integer);
|
|
begin
|
|
beginUpdate;
|
|
fheight:=v;
|
|
endUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_Sprite.setTexture(s: TD3DHook_Texture);
|
|
begin
|
|
BeginUpdate;
|
|
ftexture:=s;
|
|
width:=s.width;
|
|
height:=s.height;
|
|
EndUpdate;
|
|
end;
|
|
|
|
procedure TD3DHook_Sprite.UpdateRenderCommand;
|
|
var index: integer;
|
|
begin
|
|
index:=getindex;
|
|
|
|
owner.beginCommandListUpdate;
|
|
|
|
if (index<>-1) and (texture<>nil) and (fVisible) then
|
|
begin
|
|
if owner.renderCommandList^[index].command<>integer(rcDrawSprite) then //something completely new
|
|
owner.renderCommandList^[index].command:=integer(rcIgnored);
|
|
|
|
owner.renderCommandList^[index].x:=x;
|
|
owner.renderCommandList^[index].y:=y;
|
|
owner.renderCommandList^[index].rotation:=degtorad(rotation);
|
|
owner.renderCommandList^[index].centerX:=centerx;
|
|
owner.renderCommandList^[index].centerY:=centery;
|
|
|
|
owner.renderCommandList^[index].alphablend:=alphablend;
|
|
owner.renderCommandList^[index].Sprite.width:=width;
|
|
owner.renderCommandList^[index].Sprite.height:=height;
|
|
owner.renderCommandList^[index].Sprite.textureid:=texture.getID;
|
|
owner.renderCommandList^[index].command:=integer(rcDrawSprite);
|
|
end
|
|
else
|
|
owner.renderCommandList^[index].command:=integer(rcIgnored);
|
|
|
|
|
|
|
|
owner.endCommandListUpdate;
|
|
end;
|
|
|
|
constructor TD3DHook_Sprite.create(owner: TD3DHook; texture: TD3DHook_Texture);
|
|
begin
|
|
inherited create(owner);
|
|
beginupdate;
|
|
|
|
|
|
setTexture(texture);
|
|
endUpdate;
|
|
end;
|
|
|
|
//-------------------------------d3dhook_font-----------------------------------
|
|
|
|
function TD3DHook_FontMap.calculateFontWidth(s:string): integer;
|
|
var cwa: PWordarray;
|
|
i: integer;
|
|
c: byte;
|
|
begin
|
|
result:=0;
|
|
cwa:=localfontmapcopy.memory;
|
|
for i:=1 to length(s) do
|
|
begin
|
|
c:=ord(s[i]);
|
|
if c in [32..127] then
|
|
inc(result,cwa[c-32+1]);
|
|
end;
|
|
end;
|
|
|
|
|
|
|
|
procedure FixAlpha(aPNG: TPortableNetworkGraphic);
|
|
type
|
|
TColor32 = packed record
|
|
B, G, R, A: Byte;
|
|
end;
|
|
PColor32=^TColor32;
|
|
TColor32Array = array[0..0] of TColor32;
|
|
PColor32Array = ^TColor32Array;
|
|
var
|
|
x, y: Integer;
|
|
Line: PColor32Array;
|
|
|
|
R,G,B: Integer;
|
|
begin
|
|
|
|
R := aPng.TransparentColor and $ff;
|
|
G := (aPng.TransparentColor shr 8) and $ff;
|
|
B := (aPng.TransparentColor shr 16) and $ff;
|
|
|
|
aPNG.BeginUpdate;
|
|
for y := 0 to aPNG.Height - 1 do
|
|
begin
|
|
{$warn 5044 off}
|
|
Line := aPNG.ScanLine[y];
|
|
for x := 0 to aPNG.Width - 1 do
|
|
begin
|
|
if (Line^[x].R=R) and (Line^[x].G=G) and (Line^[x].B=B) then
|
|
Line^[x].A := 0 //100% see through
|
|
else
|
|
Line^[x].A := 255;
|
|
end;
|
|
{$warn 5044 on}
|
|
end;
|
|
aPNG.EndUpdate;
|
|
end;
|
|
|
|
|
|
procedure TD3DHook_FontMap.ChangeFont(font: TFont);
|
|
var s: string;
|
|
i: integer;
|
|
charpos: integer;
|
|
|
|
p: TPicture;
|
|
|
|
|
|
charwidth: word; //65535 if the max size of one character
|
|
|
|
newblock: pointer;
|
|
x: ptruint;
|
|
begin
|
|
p:=TPicture.Create;
|
|
p.PNG.PixelFormat:=pf32bit;
|
|
p.png.canvas.font:=font;
|
|
p.png.canvas.brush.color:=InvertColor(font.color);
|
|
p.png.Transparent:=true;
|
|
p.png.TransparentColor:=p.png.canvas.brush.color;
|
|
p.png.width:=100;
|
|
p.png.height:=100; //initial setup
|
|
|
|
//create a fontmap picture with this font and set that as the texture
|
|
//also fill in the fontmap layout
|
|
|
|
s:='';
|
|
for i:=32 to 127 do
|
|
s:=s+chr(i);
|
|
|
|
|
|
fheight:=p.png.canvas.GetTextHeight(s); //get the max height
|
|
|
|
|
|
//calculate the width based on a per character rendering. (perhaps the whole string might have been using a half pixel at some spots...)
|
|
fwidth:=0;
|
|
for i:=32 to 127 do
|
|
fwidth:=fwidth+p.png.canvas.GetTextWidth(chr(i));
|
|
|
|
|
|
//setup the image
|
|
|
|
p.png.Width:=fwidth;
|
|
p.png.height:=fheight;
|
|
|
|
if localfontmapcopy<>nil then
|
|
freeandnil(localfontmapcopy);
|
|
|
|
localfontmapcopy:=tmemorystream.create; //buffer to obtain the fontmap data
|
|
localfontmapcopy.WriteBuffer(fheight, sizeof(word)); //2 bytes for the height of all characters
|
|
|
|
//now fill it in
|
|
charpos:=0;
|
|
for i:=32 to 127 do
|
|
begin
|
|
p.PNG.canvas.TextOut(charpos, 0, chr(i));
|
|
charwidth:=p.png.Canvas.GetTextWidth(chr(i));
|
|
localfontmapcopy.WriteBuffer(charwidth,2);
|
|
charpos:=charpos+charwidth;
|
|
end;
|
|
|
|
//laz 1.6 makes 32bit png's 100% transparant
|
|
//so manually make it visible
|
|
FixAlpha(p.PNG);
|
|
|
|
newblock:=pointer(owner.memman.alloc(localfontmapcopy.size));
|
|
|
|
|
|
if newblock<>nil then
|
|
begin
|
|
if WriteProcessMemory(processhandle, newblock, localfontmapcopy.memory, localfontmapcopy.size, x) then
|
|
begin
|
|
owner.beginTextureUpdate;
|
|
|
|
//set the font state
|
|
|
|
owner.tea[id].AddressOfFontmap:=qword(newblock);
|
|
owner.tea[id].hasBeenUpdated:=1;
|
|
|
|
//free old block if possible
|
|
if (resource<>0) then
|
|
owner.memman.dealloc(fontmapdata);
|
|
|
|
|
|
LoadTextureByPicture(p);
|
|
owner.endTextureUpdate;
|
|
|
|
fontmapdata:=qword(newblock);
|
|
end;
|
|
end;
|
|
|
|
// p.SaveToFile('c:\bla.png');
|
|
end;
|
|
|
|
destructor TD3DHook_FontMap.destroy;
|
|
begin
|
|
if fontmapdata<>0 then
|
|
owner.memman.dealloc(fontmapdata);
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
constructor TD3DHook_FontMap.Create(owner: TD3DHook; font: TFont);
|
|
begin
|
|
inherited create(owner);
|
|
ChangeFont(font);
|
|
|
|
end;
|
|
|
|
|
|
//-------------------------------d3dhook_texture--------------------------------
|
|
procedure TD3DHook_Texture.LoadTextureByPicture(picture: TPicture);
|
|
var m: tmemorystream;
|
|
newblock: pointer;
|
|
x: ptruint;
|
|
msp: pointer;
|
|
s: integer;
|
|
begin
|
|
m:=TMemoryStream.create;
|
|
picture.PNG.SaveToStream(m);
|
|
|
|
owner.beginTextureUpdate;
|
|
|
|
newblock:=pointer(owner.memman.alloc(m.size));
|
|
if newblock<>nil then
|
|
begin
|
|
msp:=m.Memory;
|
|
s:=m.size;
|
|
if WriteProcessMemory(processhandle, newblock, m.memory, m.size, x) then
|
|
begin
|
|
owner.tea[id].AddressOfTexture:=qword(newblock);
|
|
owner.tea[id].Size:=m.size;
|
|
owner.tea[id].hasBeenUpdated:=1;
|
|
|
|
//free old block if possible
|
|
if (resource<>0) then
|
|
owner.memman.dealloc(resource);
|
|
|
|
fheight:=picture.Height;
|
|
fwidth:=picture.Width;
|
|
end;
|
|
|
|
resource:=qword(newblock);
|
|
end;
|
|
|
|
m.free;
|
|
|
|
owner.endTextureUpdate;
|
|
end;
|
|
|
|
function TD3DHook_Texture.getID: integer;
|
|
begin
|
|
result:=id;
|
|
end;
|
|
|
|
destructor TD3DHook_Texture.destroy;
|
|
begin
|
|
owner.beginTextureUpdate;
|
|
|
|
owner.tea[id].AddressOfTexture:=0;
|
|
owner.tea[id].hasBeenUpdated:=1; //marks for deletion
|
|
|
|
if resource<>0 then
|
|
owner.memman.dealloc(resource);
|
|
|
|
owner.textures[id]:=nil;
|
|
owner.endTextureUpdate;
|
|
inherited destroy;
|
|
end;
|
|
|
|
constructor TD3DHook_Texture.Create(owner: TD3DHook);
|
|
var i: integer;
|
|
begin
|
|
self.owner:=owner;
|
|
id:=-1;
|
|
|
|
//find a empty slot, and if not found, add it instead
|
|
for i:=0 to owner.textures.count-1 do
|
|
if owner.textures[i]=nil then
|
|
begin
|
|
id:=i;
|
|
owner.textures[id]:=self;
|
|
break;
|
|
end;
|
|
|
|
if id=-1 then
|
|
id:=owner.textures.add(self);
|
|
end;
|
|
|
|
constructor TD3DHook_Texture.Create(owner: TD3DHook; picture: TPicture);
|
|
var m: TMemoryStream;
|
|
begin
|
|
create(owner);
|
|
|
|
LoadTextureByPicture(picture);
|
|
end;
|
|
|
|
|
|
|
|
//----------------------------------D3dhook-------------------------------------
|
|
|
|
procedure TD3DHook.setSnapshotOptions(path: string; full, small: dword; Progressive: boolean; cleardepthbuffer: boolean; savepng: boolean; pictureFormat: integer);
|
|
begin
|
|
|
|
|
|
|
|
shared.snapshotdir:=IncludeTrailingPathDelimiter(path);
|
|
shared.snapshotKey:=full;
|
|
|
|
|
|
if Progressive then
|
|
begin
|
|
shared.progressiveSnapshot:=1;
|
|
shared.smallSnapshotKey:=0;
|
|
end
|
|
else
|
|
begin
|
|
shared.progressiveSnapshot:=0;
|
|
shared.smallSnapshotKey:=small;
|
|
end;
|
|
|
|
|
|
shared.snapshotImageFormat:=pictureFormat;
|
|
|
|
if cleardepthbuffer then shared.alsoClearDepthBuffer:=1 else shared.alsoClearDepthBuffer:=0;
|
|
if savepng then shared.savePNGSeperateAsWell:=1 else shared.savePNGSeperateAsWell:=0;
|
|
|
|
|
|
end;
|
|
|
|
procedure TD3DHook.setOnKeyDown(s: TD3DKeyDownEvent);
|
|
begin
|
|
if assigned(s) then
|
|
begin
|
|
fonKeyDown:=s;
|
|
shared.hasOnKey:=1;
|
|
end
|
|
else
|
|
shared.hasOnKey:=0;
|
|
|
|
end;
|
|
|
|
procedure TD3DHook.enableConsole(virtualkey: DWORD);
|
|
begin
|
|
shared.console.hasconsole:=1;
|
|
shared.console.consolekey:=virtualkey;
|
|
end;
|
|
|
|
function TD3DHook.getDisabledZBuffer: boolean;
|
|
begin
|
|
result:=shared.disabledzbuffer=1;
|
|
end;
|
|
|
|
procedure TD3DHook.setDisabledZBuffer(state: boolean);
|
|
begin
|
|
if state then
|
|
shared.disabledzbuffer:=1
|
|
else
|
|
shared.disabledzbuffer:=0;
|
|
end;
|
|
|
|
function TD3DHook.getWireframeMode: boolean;
|
|
begin
|
|
result:=shared.wireframe=1;
|
|
end;
|
|
|
|
procedure TD3DHook.setWireframeMode(state: boolean);
|
|
begin
|
|
if state then
|
|
shared.wireframe:=1
|
|
else
|
|
shared.wireframe:=0;
|
|
end;
|
|
|
|
function TD3DHook.getMouseClip: boolean;
|
|
begin
|
|
result:=shared.clipmouseinwindow=1;
|
|
end;
|
|
|
|
procedure TD3DHook.setMouseClip(state: boolean);
|
|
begin
|
|
if state then
|
|
shared.clipmouseinwindow:=1
|
|
else
|
|
shared.clipmouseinwindow:=0;
|
|
end;
|
|
|
|
function TD3DHook.getWidth: integer;
|
|
var x: trect;
|
|
begin
|
|
if (shared<>nil) and (GetClientRect(shared.lastHwnd, x)) then
|
|
result:=x.Right-x.left
|
|
else
|
|
result:=0;
|
|
end;
|
|
|
|
function TD3DHook.getHeight: integer;
|
|
var x: trect;
|
|
begin
|
|
if (shared<>nil) and (GetClientRect(shared.lastHwnd, x)) then
|
|
result:=x.bottom-x.top
|
|
else
|
|
result:=0;
|
|
end;
|
|
|
|
procedure TD3DHook.setCommandListLockFeature(state: boolean);
|
|
begin
|
|
shared.useCommandListLock:=integer(state);
|
|
end;
|
|
|
|
procedure TD3DHook.beginCommandListUpdate;
|
|
begin
|
|
if self=nil then
|
|
raise exception.create(rsTheD3dhookObjectHasNotBeenCreatedYet);
|
|
|
|
commandlistCS.enter;
|
|
|
|
if isupdatingCL=0 then //start of an edit
|
|
WaitForSingleObject(CommandListLock, INFINITE); //obtain lock
|
|
|
|
inc(isupdatingCL);
|
|
end;
|
|
|
|
procedure TD3DHook.endCommandListUpdate;
|
|
begin
|
|
renderCommandList^[commandlist.count].command:=integer(rcEndOfCommandlist);
|
|
|
|
if isupdatingCL>0 then
|
|
begin
|
|
dec(isupdatingCL);
|
|
if isupdatingCL=0 then
|
|
SetEvent(CommandListLock); //release the lock
|
|
end;
|
|
|
|
|
|
commandlistCS.leave;
|
|
|
|
|
|
end;
|
|
|
|
procedure TD3DHook.beginTextureUpdate;
|
|
begin
|
|
if isupdating=0 then //start of an edit
|
|
begin
|
|
beginCommandListUpdate;
|
|
WaitForSingleObject(TextureLock, INFINITE); //obtain lock
|
|
end;
|
|
|
|
inc(isupdating);
|
|
end;
|
|
|
|
procedure TD3DHook.endTextureUpdate;
|
|
begin
|
|
if isupdating>0 then
|
|
begin
|
|
dec(isupdating);
|
|
if isupdating=0 then
|
|
begin
|
|
shared.textureCount:=textures.count;
|
|
shared.texturelistHasUpdate:=1;
|
|
|
|
SetEvent(TextureLock); //release the lock
|
|
|
|
endCommandListUpdate;
|
|
end;
|
|
end;
|
|
end;
|
|
|
|
|
|
//++++++++++++++++++++++++++++++++++++++++++++++++++++++version 2
|
|
function TD3DHook.createFontMap(f: TFont): TD3DHook_FontMap;
|
|
begin
|
|
beginTextureUpdate;
|
|
result:=TD3DHook_FontMap.Create(self, f);
|
|
endTextureUpdate;
|
|
end;
|
|
|
|
function TD3DHook.createTextContainer(fontmap: TD3DHook_FontMap; x,y: single; text: string): TD3Dhook_TextContainer;
|
|
begin
|
|
beginCommandListUpdate;
|
|
try
|
|
result:=TD3Dhook_TextContainer.create(self, fontmap, x,y,text);
|
|
finally
|
|
endCommandListUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TD3DHook.createTexture(f: string): TD3DHook_Texture; overload;
|
|
var p: TPicture;
|
|
begin
|
|
p:=tpicture.Create;
|
|
beginTextureUpdate;
|
|
try
|
|
p.LoadFromFile(f);
|
|
result:=TD3DHook_Texture.Create(self, p);
|
|
finally
|
|
p.free;
|
|
endTextureUpdate;
|
|
end;
|
|
end;
|
|
|
|
function TD3DHook.createTexture(p: TPicture): TD3DHook_Texture;
|
|
begin
|
|
beginTextureUpdate;
|
|
result:=TD3DHook_Texture.Create(self, p);
|
|
endTextureUpdate;
|
|
end;
|
|
|
|
function TD3DHook.createSprite(texture: TD3DHook_Texture): TD3DHook_Sprite;
|
|
begin
|
|
beginCommandListUpdate;
|
|
result:=TD3DHook_Sprite.create(self, texture);
|
|
endCommandListUpdate;
|
|
end;
|
|
|
|
|
|
destructor TD3DHook.Destroy;
|
|
var i: integer;
|
|
begin
|
|
if messagehandler<>nil then
|
|
begin
|
|
messagehandler.terminate;
|
|
messagehandler.Free;
|
|
end;
|
|
|
|
if hooked then
|
|
begin
|
|
|
|
beginCommandListUpdate;
|
|
|
|
for i:=0 to commandlist.Count-1 do
|
|
if commandlist[i]<>nil then
|
|
TD3DHook_RenderObject(commandlist[i]).free;
|
|
|
|
//make sure all commands are gone:
|
|
if commandlist.count>0 then
|
|
renderCommandList^[0].command:=integer(rcIgnored);
|
|
|
|
endCommandListUpdate;
|
|
|
|
|
|
beginTextureUpdate;
|
|
|
|
for i:=0 to textures.Count-1 do
|
|
if textures[i]<>nil then
|
|
TD3DHook_Texture(textures[i]).Free;
|
|
|
|
endTextureUpdate;
|
|
|
|
end;
|
|
|
|
UnmapViewOfFile(shared);
|
|
closehandle(fmhandle);
|
|
|
|
commandlist.free;
|
|
textures.free;
|
|
|
|
memman.free;
|
|
|
|
d3dhook:=nil;
|
|
|
|
inherited destroy;
|
|
end;
|
|
|
|
constructor TD3DHook.create(size: integer; hookhwnd: boolean=true);
|
|
var h: thandle;
|
|
s: TStringList;
|
|
|
|
alreadyhooked: boolean;
|
|
begin
|
|
memman:=TRemoteMemoryManager.create;
|
|
textures:=TList.create;
|
|
commandlist:=TList.create;
|
|
|
|
commandlistCS:=TCriticalSection.create;
|
|
|
|
|
|
sharename:='CED3D_'+inttostr(processhandler.ProcessID);
|
|
|
|
fprocessid:=processhandler.processid;
|
|
maxsize:=size;
|
|
|
|
createSharedMemory(sharename, sizeof(TD3DHookShared)+maxsize);
|
|
|
|
fmhandle:=OpenFileMapping(FILE_MAP_EXECUTE or FILE_MAP_READ or FILE_MAP_WRITE, false, pchar(sharename));
|
|
if fmhandle=0 then
|
|
raise exception.create(rsD3DHookFailureToOpenTheSharedMemoryObject);
|
|
|
|
shared:=MapViewOfFile(fmhandle,FILE_MAP_EXECUTE or FILE_MAP_READ or FILE_MAP_WRITE, 0,0,0 );
|
|
|
|
if shared=nil then
|
|
raise exception.create(rsD3DHookFailureToMapTheSharedMemoryObject);
|
|
|
|
alreadyhooked:=shared.initialized=$dbcedbce;
|
|
|
|
if not alreadyhooked then
|
|
begin
|
|
ZeroMemory(shared, sizeof(TD3DHookShared));
|
|
shared.texturelist:=sizeof(TD3DHookShared)+(maxsize div 2);
|
|
shared.cheatenginedir:=CheatEngineDir;
|
|
shared.useCommandListLock:=1;
|
|
end;
|
|
|
|
|
|
tea:=PTextureEntryArray(ptruint(shared)+shared.texturelist);
|
|
renderCommandList:=PRenderCommandArray(ptruint(shared)+sizeof(TD3dHookShared));
|
|
|
|
if alreadyhooked then
|
|
begin
|
|
DuplicateHandle(processhandle, shared.TextureLock, GetCurrentProcess, @TextureLock, 0, false,DUPLICATE_SAME_ACCESS);
|
|
DuplicateHandle(processhandle, shared.CommandListLock, GetCurrentProcess, @CommandListLock, 0, false,DUPLICATE_SAME_ACCESS);
|
|
DuplicateHandle(processhandle, shared.SnapshotDone, GetCurrentProcess, @SnapshotDone, 0, false,DUPLICATE_SAME_ACCESS);
|
|
|
|
beginTextureUpdate;
|
|
renderCommandList^[0].command:=integer(rcEndOfCommandlist); //clear the command list
|
|
shared.textureCount:=0;
|
|
endTextureUpdate;
|
|
end
|
|
else
|
|
begin
|
|
if hookhwnd then
|
|
shared.hookwnd:=1;
|
|
|
|
h:=CreateEventA(nil, true, false, pchar(sharename+'_READY') );
|
|
|
|
if (h<>0) then
|
|
begin
|
|
|
|
if hookhwnd then
|
|
begin
|
|
hasclickevent:=CreateEventA(nil, false, false, pchar(sharename+'_HASCLICK') );
|
|
hashandledclickevent:=CreateEventA(nil, false, true, pchar(sharename+'_HANDLEDCLICK') );
|
|
|
|
haskeyboardevent:=CreateEventA(nil, false, false, pchar(sharename+'_HASKEYBOARD') );
|
|
hashandledkeyboardevent:=CreateEventA(nil, false, true, pchar(sharename+'_HANDLEDKEYBOARD') );
|
|
|
|
|
|
messagehandler:=TD3DMessageHandler.Create(true);
|
|
messagehandler.owner:=self;
|
|
messagehandler.start;
|
|
end;
|
|
|
|
|
|
TextureLock:=CreateEventA(nil, false, true, nil);
|
|
DuplicateHandle(GetCurrentProcess, TextureLock, processhandle, @shared.TextureLock,0, false,DUPLICATE_SAME_ACCESS);
|
|
|
|
CommandListLock:=CreateEventA(nil, false, true, nil);
|
|
DuplicateHandle(GetCurrentProcess, CommandListLock, processhandle, @shared.CommandListLock,0, false,DUPLICATE_SAME_ACCESS);
|
|
|
|
SnapshotDone:=CreateEventA(nil, false, false, nil);
|
|
DuplicateHandle(GetCurrentProcess, SnapshotDone, processhandle, @shared.SnapshotDone,0, false,DUPLICATE_SAME_ACCESS);
|
|
|
|
shared.canDoSnapshot:=1;
|
|
|
|
|
|
|
|
//now inject the dll
|
|
symhandler.reinitialize;
|
|
symhandler.waitforsymbolsloaded(true, 'kernel32.dll');
|
|
if processhandler.is64Bit then
|
|
injectdll(cheatenginedir+'d3dhook64.dll')
|
|
else
|
|
injectdll(cheatenginedir+'d3dhook.dll');
|
|
|
|
//wait till the injection is done
|
|
WaitForSingleObject(h, INFINITE);
|
|
closehandle(h);
|
|
|
|
//and hook the functions
|
|
//(script: tstrings; address: string; addresstogoto: string; addresstostoreneworiginalfunction: string=''; nameextension:string='0');
|
|
s:=tstringlist.create;
|
|
try
|
|
if shared.d3d9_present<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_present,8), inttohex(shared.d3d9_newpresent,8), inttohex(shared.d3d9_originalpresent,8), '0');
|
|
|
|
if shared.d3d9_reset<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_reset,8), inttohex(shared.d3d9_newreset,8), inttohex(shared.d3d9_originalreset,8), '1');
|
|
|
|
if shared.dxgi_present<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.dxgi_present,8), inttohex(shared.dxgi_newpresent,8), inttohex(shared.dxgi_originalpresent,8), '2');
|
|
|
|
if shared.d3d9_drawprimitive<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawprimitive,8), inttohex(shared.d3d9_newdrawprimitive,8), inttohex(shared.d3d9_originaldrawprimitive,8), '3');
|
|
|
|
if shared.d3d9_drawindexedprimitive<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawindexedprimitive,8), inttohex(shared.d3d9_newdrawindexedprimitive,8), inttohex(shared.d3d9_originaldrawindexedprimitive,8), '4');
|
|
|
|
if shared.d3d9_drawprimitiveup<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawprimitiveup,8), inttohex(shared.d3d9_newdrawprimitiveup,8), inttohex(shared.d3d9_originaldrawprimitiveup,8), '5');
|
|
|
|
if shared.d3d9_drawindexedprimitiveup<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawindexedprimitiveup,8), inttohex(shared.d3d9_newdrawindexedprimitiveup,8), inttohex(shared.d3d9_originaldrawindexedprimitiveup,8), '6');
|
|
|
|
if shared.d3d9_drawrectpatch<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawrectpatch,8), inttohex(shared.d3d9_newdrawrectpatch,8), inttohex(shared.d3d9_originaldrawrectpatch,8), '7');
|
|
|
|
if shared.d3d9_drawtripatch<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d9_drawtripatch,8), inttohex(shared.d3d9_newdrawtripatch,8), inttohex(shared.d3d9_originaldrawtripatch,8), '8');
|
|
|
|
|
|
if shared.d3d10_drawindexed<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d10_drawindexed,8), inttohex(shared.d3d10_newdrawindexed,8), inttohex(shared.d3d10_originaldrawindexed,8), '9');
|
|
|
|
if shared.d3d10_draw<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d10_draw,8), inttohex(shared.d3d10_newdraw,8), inttohex(shared.d3d10_originaldraw,8), '10');
|
|
|
|
if shared.d3d10_drawindexedinstanced<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d10_drawindexedinstanced,8), inttohex(shared.d3d10_newdrawindexedinstanced,8), inttohex(shared.d3d10_originaldrawindexedinstanced,8), '11');
|
|
|
|
if shared.d3d10_drawinstanced<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d10_drawinstanced,8), inttohex(shared.d3d10_newdrawinstanced,8), inttohex(shared.d3d10_originaldrawinstanced,8), '12');
|
|
|
|
if shared.d3d10_drawauto<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d10_drawauto,8), inttohex(shared.d3d10_newdrawauto,8), inttohex(shared.d3d10_originaldrawauto,8), '13');
|
|
|
|
|
|
if shared.d3d11_drawindexed<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d11_drawindexed,8), inttohex(shared.d3d11_newdrawindexed,8), inttohex(shared.d3d11_originaldrawindexed,8), '14');
|
|
|
|
if shared.d3d11_draw<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d11_draw,8), inttohex(shared.d3d11_newdraw,8), inttohex(shared.d3d11_originaldraw,8), '15');
|
|
|
|
if shared.d3d11_drawindexedinstanced<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d11_drawindexedinstanced,8), inttohex(shared.d3d11_newdrawindexedinstanced,8), inttohex(shared.d3d11_originaldrawindexedinstanced,8), '16');
|
|
|
|
if shared.d3d11_drawinstanced<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d11_drawinstanced,8), inttohex(shared.d3d11_newdrawinstanced,8), inttohex(shared.d3d11_originaldrawinstanced,8), '17');
|
|
|
|
if shared.d3d11_drawauto<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.d3d11_drawauto,8), inttohex(shared.d3d11_newdrawauto,8), inttohex(shared.d3d11_originaldrawauto,8), '18');
|
|
|
|
if shared.dxgi_resizebuffers<>0 then
|
|
generateAPIHookScript(s, inttohex(shared.dxgi_resizebuffers,8), inttohex(shared.dxgi_newresizebuffers,8), inttohex(shared.dxgi_originalresizebuffers,8), '19');
|
|
|
|
|
|
// clipboard.AsText:=s.text;
|
|
|
|
//if there is a script execute it.
|
|
if (s.count>0) and (autoassemble(s,false)=false) then
|
|
begin
|
|
//on error write the script to the clipboard
|
|
clipboard.AsText:=s.text; //debug
|
|
end;
|
|
|
|
shared.initialized:=$dbcedbce;
|
|
finally
|
|
s.free;
|
|
end;
|
|
|
|
|
|
end;
|
|
end;
|
|
|
|
|
|
hooked:=true;
|
|
|
|
end;
|
|
|
|
function safed3dhook(size: integer=16*1024*1024; hookwindow: boolean=true): TD3DHook;
|
|
//Calls the d3dhook constructor but captures exceptions
|
|
var wr: DWORD;
|
|
begin
|
|
if d3dhook=nil then
|
|
begin
|
|
try
|
|
d3dhook:=TD3DHook.Create(size, hookwindow);
|
|
except
|
|
d3dhook:=nil;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
if d3dhook.processid<>processid then //different process
|
|
begin
|
|
d3dhook.Free;
|
|
try
|
|
d3dhook:=TD3DHook.Create(size, hookwindow);
|
|
except
|
|
d3dhook:=nil;
|
|
end;
|
|
end
|
|
else
|
|
begin
|
|
//same process
|
|
wr:=WaitForSingleObject(processhandle, 0);
|
|
if wr<>WAIT_TIMEOUT then //not alive anymore
|
|
freeandnil(d3dhook);
|
|
|
|
end;
|
|
|
|
end;
|
|
|
|
result:=d3dhook;
|
|
|
|
end;
|
|
|
|
{$endif}
|
|
|
|
end.
|
|
|