library HyperOverride;
{$R *.res}
uses
SysUtils,
StrUtils,
Classes,
Windows,
pelib in 'pelib.pas';
type
TSearch = record
Handle: THANDLE;
Index: Integer;
Datas: Array of _WIN32_FIND_DATAA;
end;
PSearch = ^TSearch;
var
HOName: String;
TabState: Boolean = False;
s: String;
i: Integer;
Searches: TList = nil;
CodePage: Word;
Cmd: String;
function WideStrToAnsiStr(const ws: WideString): AnsiString;
var
l: Integer;
begin
if ws = '' then
Result := ''
else
begin
l := WideCharToMultiByte(CodePage, WC_COMPOSITECHECK or WC_DISCARDNS or WC_SEPCHARS or WC_DEFAULTCHAR, PWideChar(ws), -1, nil, 0, nil, nil);
SetLength(Result, l - 1);
if l > 1 then
WideCharToMultiByte(CodePage, WC_COMPOSITECHECK or WC_DISCARDNS or WC_SEPCHARS or WC_DEFAULTCHAR, PWideChar(ws), -1, PAnsiCHar(Result), l - 1, nil, nil);
end;
end;
function AnsiStrToWideStr(const s: AnsiString): WideString;
var
l: Integer;
begin
if s = '' then
Result := ''
else
begin
l := MultiByteToWideChar(CodePage, MB_PRECOMPOSED, PAnsiCHar(s), -1, nil, 0);
SetLength(Result, l - 1);
if l > 1 then
MultiByteToWideChar(CodePage, MB_PRECOMPOSED, PAnsiCHar(s), -1, PWideChar(Result), l - 1);
end;
end;
function MyFindFirstFileA(lpFileName: PAnsiCHar; var lpFindFileData: _WIN32_FIND_DATAA): THANDLE; stdcall;
var
FileName: AnsiString;
FindFileData: _WIN32_FIND_DATAA;
Handle: THANDLE;
HOHandle: THANDLE;
Search: PSearch;
NotFound: Boolean;
i: Integer;
begin
if AnsiCompareText(ExtractFileDir(AnsiStrToWideStr(lpFileName)), GetCurrentDir + '\override') = 0 then
begin
Search := nil;
Handle := FindFirstFileA(lpFileName, FindFileData);
Result := Handle;
if Handle <> INVALID_HANDLE_VALUE then
begin
if Searches = nil then
Searches := TList.Create;
New(Search);
Searches.Add(Search);
Search.Handle := Handle;
repeat
SetLength(Search.Datas, Length(Search.Datas) + 1);
Move(FindFileData, Search.Datas[Length(Search.Datas) - 1], SizeOf(_WIN32_FIND_DATAA));
until not FindNextFileA(Handle, FindFileData);
end;
FileName := WideStrToAnsiStr(HOName + ExtractFileName(AnsiStrToWideStr(lpFileName)));
HOHandle := FindFirstFileA(PAnsiCHar(FileName), FindFileData);
if HOHandle <> INVALID_HANDLE_VALUE then
begin
Result := HOHandle;
if Handle <> INVALID_HANDLE_VALUE then
FindClose(Handle)
else
begin
if Searches = nil then
Searches := TList.Create;
New(Search);
Searches.Add(Search);
end;
Search.Handle := HOHandle;
repeat
NotFound := True;
for i := 0 to Length(Search.Datas) - 1 do
if AnsiCompareText(AnsiStrToWideStr(Search.Datas[i].cFileName), AnsiStrToWideStr(FindFileData.cFileName)) = 0 then
begin
Move(FindFileData, Search.Datas[i], SizeOf(_WIN32_FIND_DATAA));
NotFound := False;
break;
end;
if NotFound then
begin
SetLength(Search.Datas, Length(Search.Datas) + 1);
Move(FindFileData, Search.Datas[Length(Search.Datas) - 1], SizeOf(_WIN32_FIND_DATAA));
end;
until not FindNextFileA(HOHandle, FindFileData);
end;
if Search <> nil then
begin
Search.Index := 1;
Move(Search.Datas[0], lpFindFileData, SizeOf(_WIN32_FIND_DATAA));
end;
end
else
Result := FindFirstFileA(lpFileName, lpFindFileData);
end;
function MyFindNextFileA(hFindFile: THANDLE; var lpFindFileData: _WIN32_FIND_DATAA): Longbool; stdcall;
var
i: Integer;
Search: PSearch;
begin
if Searches <> nil then
for i := 0 to Searches.Count - 1 do
begin
Search := PSearch(Searches[i]);
if Search.Handle = hFindFile then
begin
Result := Length(Search.Datas) >= Search.Index + 1;
if Result then
begin
Move(Search.Datas[Search.Index], lpFindFileData, SizeOf(_WIN32_FIND_DATAA));
Inc(Search.Index);
end;
Exit;
end;
end;
Result := FindNextFileA(hFindFile, lpFindFileData);
end;
function MyFindClose(hFindFile: THANDLE): Longbool; stdcall;
var
i: Integer;
Search: PSearch;
begin
if Searches <> nil then
for i := 0 to Searches.Count - 1 do
begin
Search := PSearch(Searches[i]);
if Search.Handle = hFindFile then
begin
Dispose(Search);
Searches.Delete(i);
if Searches.Count = 0 then
begin
Searches.Free;
Searches := nil;
end;
break;
end;
end;
Result := FindClose(hFindFile);
end;
function MyCreateFileA(lpFileName: PAnsiCHar; dwDesiredAccess: Longword; dwShareMode: Longword; lpSecurityAttributes: Pointer; dwCreationDisposition: Longword; dwFlagsAndAttributes: Longword; hTemplateFile: THANDLE): THANDLE; stdcall;
var
FileName: AnsiString;
begin
if AnsiCompareText(ExtractFileDir(AnsiStrToWideStr(lpFileName)), GetCurrentDir + '\override') = 0 then
begin
FileName := WideStrToAnsiStr(HOName + ExtractFileName(AnsiStrToWideStr(lpFileName)));
if FileExists(AnsiStrToWideStr(FileName)) then
begin
Result := CreateFileA(PAnsiCHar(FileName), dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
Exit;
end;
end;
Result := CreateFileA(lpFileName, dwDesiredAccess, dwShareMode, lpSecurityAttributes, dwCreationDisposition, dwFlagsAndAttributes, hTemplateFile);
end;
function MyGetAsyncKeyState(const vKey: Longint): Smallint; stdcall;
begin
Result := GetAsyncKeyState(vKey);
if vKey <> 9 then
Exit;
if Result and 1 = 1 then
TabState := not TabState;
if TabState then
Result := -32768
else
Result := 0;
end;
function HookIAT(ModuleName: PCHar; FunctionName: PCHar; NewAddress: Pointer): Boolean;
var
hd: HModule;
dosHeader: PIMAGE_DOS_HEADER;
ntHeader: PIMAGE_NT_HEADERS;
ImportDesc: PIMAGE_IMPORT_DESCRIPTOR;
pThunk: PIMAGE_THUNK_DATA;
OriginalFunction: DWORD;
OldFlag: DWORD;
begin
Result := False;
hd := GetModuleHandle(nil);
OriginalFunction := DWORD(GetProcAddress(GetModuleHandle(ModuleName), FunctionName));
if OriginalFunction = 0 then
Exit;
dosHeader := PIMAGE_DOS_HEADER(hd);
if dosHeader.e_magic = IMAGE_DOS_SIGNATURE then
begin
ntHeader := PIMAGE_NT_HEADERS(DWORD(dosHeader) + DWORD(dosHeader.e_lfanew));
if ntHeader.Signature <> IMAGE_NT_SIGNATURE then
Exit
else
begin
ImportDesc := PIMAGE_IMPORT_DESCRIPTOR(DWORD(dosHeader) + ntHeader.OptionalHeader.DataDirectory[IMAGE_DIRECTORY_ENTRY_IMPORT].VirtualAddress);
if ImportDesc = PIMAGE_IMPORT_DESCRIPTOR(ntHeader) then
Exit
else
while ImportDesc.Name <> 0 do
begin
if AnsiCompareText(AnsiStrToWideStr(PAnsiCHar(DWORD(dosHeader) + ImportDesc.Name)), ModuleName) = 0 then
break;
Inc(DWORD(ImportDesc), SizeOf(IMAGE_IMAGE_IMPORT_DESCRIPTOR));
end;
pThunk := PIMAGE_THUNK_DATA(DWORD(dosHeader) + ImportDesc.FirstThunk);
while pThunk._Function <> 0 do
begin
if (pThunk._Function = OriginalFunction) and VirtualProtect(@pThunk._Function, 4, PAGE_READWRITE, @OldFlag) then
begin
pThunk._Function := DWORD(NewAddress);
VirtualProtect(@pThunk._Function, 4, OldFlag, @OldFlag);
Result := True;
end;
Inc(DWORD(pThunk), SizeOf(IMAGE_THUNK_DATA));
end;
end;
end;
end;
procedure Abort;
begin
MessageBox(0,'HyperOverride has failed to modify the memory of the game. You should immediately quit the game after you close this message!','HyperOverride Error', MB_ICONERROR);
end;
begin
Cmd := AnsiLowerCase(CmdLine);
for i := 1 to ParamCount do
begin
s := LowerCase(ParamStr(i));
if s = '-tabtoggle' then
begin
if not HookIAT('user32.dll', 'GetAsyncKeyState', @MyGetAsyncKeyState) then
Abort;
end
else if Copy(s, 1, 4) = '-ho:' then
if HookIAT('kernel32.dll', 'FindFirstFileA', @MyFindFirstFileA) and HookIAT('kernel32.dll', 'FindNextFileA', @MyFindNextFileA) and HookIAT('kernel32.dll', 'FindClose', @MyFindClose) and HookIAT('kernel32.dll', 'CreateFileA', @MyCreateFileA) then
begin
HOName := 'HyperOverride\' + Copy(s, 5, Length(s)) + '\';
CodePage := GetACP;
end
else
Abort;
end;
end.