cheat-engine/Cheat Engine/csharpcompiler.pas
2021-11-16 20:58:25 +01:00

262 lines
6.7 KiB
ObjectPascal

//Copyright Cheat Engine
unit CSharpCompiler;
{$mode objfpc}{$H+}
interface
uses
{$ifdef windows}
jwawindows, windows, Classes, SysUtils, dotnethost{$ifndef standalonetest}, newkernelhandler{$endif};
{$else}
classes, SysUtils;
//todo: mono?
{$endif}
type
TCSharpCompilerError=class(exception);
TCSharpCompiler=class(TObject)
private
dCompileCode: function(script: pchar; path: string; userdata: pointer; errorcallback: pointer): boolean; stdcall;
dAddReference: procedure(path: pchar); stdcall;
dSetCoreAssembly: procedure(coreAssembly: pchar); stdcall;
dRelease: procedure(); stdcall;
public
procedure addAssemblyReference(path: string);
procedure setCoreAssembly(path: string);
function compile(script: string; path: string; errorlog: tstrings): boolean;
constructor create;
destructor destroy; override;
end;
function compilecsharp(script: string; references: tstringlist; coreAssembly: string=''): string; //compile the script and return the name of the generated file. Will raise exception on error
implementation
{$ifndef standalonetest}
uses cefuncproc, globals, mainunit2;
{$endif}
var counter: integer;
{$ifdef standalonetest}
type
PPROCESSENTRY32 = ^PROCESSENTRY32;
{$EXTERNALSYM PPROCESSENTRY32}
tagPROCESSENTRY32 = record
dwSize: DWORD;
cntUsage: DWORD;
th32ProcessID: DWORD; // this process
th32DefaultHeapID: ULONG_PTR;
th32ModuleID: DWORD; // associated exe
cntThreads: DWORD;
th32ParentProcessID: DWORD; // this process's parent process
pcPriClassBase: LONG; // Base priority of process's threads
dwFlags: DWORD;
szExeFile: array [0..MAX_PATH - 1] of Char; // Path
end;
{$EXTERNALSYM tagPROCESSENTRY32}
PROCESSENTRY32 = tagPROCESSENTRY32;
{$EXTERNALSYM PROCESSENTRY32}
LPPROCESSENTRY32 = ^PROCESSENTRY32;
{$EXTERNALSYM LPPROCESSENTRY32}
TProcessEntry32 = PROCESSENTRY32;
{$endif}
{$ifdef windows}
procedure cleanupcecsfiles;
var
DirInfo: TSearchRec;
{$ifdef standalonetest}
x: PROCESSENTRY32;
pe32: jwawindows.PROCESSENTRY32 absolute x;
{$else}
pe32: PROCESSENTRY32;
{$endif}
r : Integer;
usedtempdir: string;
i: integer;
sa: array of string;
pidlist: tstringlist;
SNAPHandle: THandle;
begin
{$ifdef standalonetest}
usedtempdir:=GetTempDir;
{$else}
if (length(trim(tempdiralternative))>2) and dontusetempdir then
usedtempdir:=trim(tempdiralternative)
else
usedtempdir:=GetTempDir;
{$endif}
usedtempdir:=IncludeTrailingPathDelimiter(usedtempdir)+strCheatEngine+pathdelim;
//first clean up my own crap
for i:=counter-1 downto 0 do
DeleteFile(usedtempdir+'ce-cscode-'+inttostr(getcurrentprocessid)+'-'+inttostr(i)+'.dll');
//and now search for other files
SNAPHandle:=CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS,0);
zeromemory(@pe32, sizeof({$ifdef standalonetest}x{$else}pe32{$endif}));
pe32.dwSize:=sizeof({$ifdef standalonetest}x{$else}pe32{$endif});
pidlist:=tstringlist.create;
pidlist.sorted:=true; //speeds up indexof
if (snaphandle<>0) and (snaphandle<>INVALID_HANDLE_VALUE) then
begin
if Process32First(snaphandle, pe32) then
repeat
pidlist.Add(inttostr(pe32.th32ProcessID));
until Process32Next(snaphandle, pe32)=false;
closehandle(SNAPHandle);
end;
ZeroMemory(@DirInfo,sizeof(DirInfo));
DirInfo.Size:=sizeof(DirInfo);
r := FindFirst(usedtempdir+'ce-cscode-*.dll', FaAnyfile, DirInfo);
while (r = 0) do
begin
if ((DirInfo.Attr and FaVolumeId) <> FaVolumeID) and
((DirInfo.Attr and FaDirectory) <> FaDirectory) then
begin
sa:=string(dirinfo.name).Split('-');
if length(sa)=4 then
begin
try
if pidlist.indexof(sa[2])=-1 then //does not exist (anymore) try to delete it
DeleteFile(usedtempdir + DirInfo.Name);
except
end;
end;
end;
r := FindNext(DirInfo);
end;
FindClose(DirInfo);
pidlist.free;
end;
{$endif}
function compilecsharp(script: string; references: tstringlist; coreAssembly:string=''): string;
{$ifdef windows}
var
c: TCSharpCompiler;
errorlog: Tstringlist;
i: integer;
usedtempdir: string;
filename: string;
{$endif}
begin
{$ifdef windows}
{$ifdef standalonetest}
usedtempdir:=GetTempDir;
{$else}
if (length(trim(tempdiralternative))>2) and dontusetempdir then
usedtempdir:=trim(tempdiralternative)
else
usedtempdir:=GetTempDir;
{$endif}
usedtempdir:=IncludeTrailingPathDelimiter(usedtempdir)+strCheatEngine+pathdelim;
inc(counter);
filename:=usedtempdir+'ce-cscode-'+inttostr(getcurrentprocessid)+'-'+inttostr(counter)+'.dll';
c:=TCSharpCompiler.create;
errorlog:=tstringlist.create;
try
for i:=0 to references.count-1 do
c.addAssemblyReference(references[i]);
if coreAssembly<>'' then
c.setCoreAssembly(coreAssembly);
if c.compile(script, filename, errorlog)=false then
raise TCSharpCompilerError.create(errorlog.text);
result:=filename;
finally
errorlog.free;
c.free;
end;
{$endif}
end;
procedure OnErrorCallback(errorlog: tstrings; s: pchar);
begin
if errorlog<>nil then
errorlog.add(s);
end;
procedure TCSharpCompiler.addAssemblyReference(path: string);
begin
if assigned(dAddReference) then
dAddReference(pchar(path));
end;
procedure TCSharpCompiler.setCoreAssembly(path: string);
begin
if assigned(dSetCoreAssembly) then
dSetCoreAssembly(pchar(path));
end;
function TCSharpCompiler.compile(script: string; path: string; errorlog: tstrings): boolean;
begin
result:=dCompileCode(pchar(script), pchar(path), errorlog, @OnErrorCallback);
end;
constructor TCSharpCompiler.create;
var
delegates: record
CompileCode: pointer;
AddReference: pointer;
SetCoreAssembly: pointer;
Release: pointer;
end;
r: integer;
begin
{$ifdef windows}
r:=DotNetExecuteClassMethod({$ifdef standalonetest}'D:\git\cheat-engine\Cheat Engine\bin\CSCompiler.dll'{$else}CheatEngineDir+'CSCompiler.dll'{$endif},'CSCompiler','Compiler','NewCompiler',inttostr(ptruint(@delegates)));
//r:=DotNetExecuteClassMethod({$ifdef standalonetest}'D:\git\cheat-engine\Cheat Engine\bin\CSCompiler.dll'{$else}CheatEngineDir+'CSCompiler.dll'{$endif},'CSCompiler','Compiler','NewCompiler',inttostr(ptruint(@delegates)));
if r<>1 then raise exception.create('C-Sharp compiler creation failed');
pointer(dCompileCode):=delegates.CompileCode;
pointer(dAddReference):=delegates.AddReference;
pointer(dSetCoreAssembly):=delegates.SetCoreAssembly;
pointer(dRelease):=delegates.Release;
{$else}
raise exception.create('C# compiler not implemented yet');
{$endif}
end;
destructor TCSharpCompiler.destroy;
begin
if assigned(dRelease) then
dRelease();
inherited destroy;
end;
finalization
{$ifdef windows}
cleanupcecsfiles;
{$endif}
end.