diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..4a97a60
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2022 Lovro Pleše
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..3152025
--- /dev/null
+++ b/README.md
@@ -0,0 +1,43 @@
+# UbiTMNTHax - for Teenage Mutant Ninja Turtles (2007/Ubisoft)
+
+This is a very simple plugin for TMNT by Ubisoft which introduces some quality of life fixes to the game.
+
+## Features
+
+- Restore native windowed mode - no need for DXWnd
+
+- Fixes native window mode cursor position reading
+
+- Allow borderless windowed mode
+
+- Allow window resizing, relocating of borderless window
+
+- Allow launching without the TMNT Launcher/HW detector
+
+- Allow launching multiple instances of the game
+
+- Allow launching of game without the disc inserted (no need for mini image in virtual CD)
+
+- Add console window for debug output
+
+## Instructions
+
+- Extract the release package on game root directory
+
+- Configure your settings in scripts\UbiTMNTHax.ini
+
+- If launching for the first time, launch the game with the regular TMNT.exe
+
+- Else, you can launch the game with TMNTGame.exe directly
+
+- For windowed mode, set Fullscreen to 0 in Hardware.ini found in the TMNT data directory (AppData\Roaming\TMNT) and then launch the game
+
+## Compatibility
+
+This was made with the executable version 1.0.0.188 (md5: 5D084D4367688490CB2FC2D2E24CF906) in mind
+
+## Credits
+
+ThirteenAG - [Ultimate ASI Loader](https://github.com/ThirteenAG/Ultimate-ASI-Loader) and [IniReader](https://github.com/ThirteenAG/IniReader)
+
+thelink2012 - [injector](https://github.com/thelink2012/injector)
diff --git a/UbiTMNTHax.filters b/UbiTMNTHax.filters
new file mode 100644
index 0000000..95c6df3
--- /dev/null
+++ b/UbiTMNTHax.filters
@@ -0,0 +1,33 @@
+
+
+
+
+ {4FC737F1-C7A5-4376-A066-2A32D752A2FF}
+ cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx
+
+
+ {93995380-89BD-4b04-88EB-625FBE52EBFB}
+ h;hh;hpp;hxx;hm;inl;inc;xsd
+
+
+ {67DA6AB6-F800-4c08-8B7A-83BB121AAD01}
+ rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms
+
+
+
+
+ Header Files
+
+
+ Header Files
+
+
+
+
+ Source Files
+
+
+ Source Files
+
+
+
\ No newline at end of file
diff --git a/UbiTMNTHax.sln b/UbiTMNTHax.sln
new file mode 100644
index 0000000..0d3d262
--- /dev/null
+++ b/UbiTMNTHax.sln
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 14
+VisualStudioVersion = 14.0.25420.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UbiTMNTHax", "UbiTMNTHax.vcxproj", "{3C558AD9-5F9C-4A14-8F07-800F46C132C7}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|x86 = Debug|x86
+ Release|x86 = Release|x86
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|x86.ActiveCfg = Debug|Win32
+ {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Debug|x86.Build.0 = Debug|Win32
+ {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|x86.ActiveCfg = Release|Win32
+ {3C558AD9-5F9C-4A14-8F07-800F46C132C7}.Release|x86.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/UbiTMNTHax.vcxproj b/UbiTMNTHax.vcxproj
new file mode 100644
index 0000000..0e59560
--- /dev/null
+++ b/UbiTMNTHax.vcxproj
@@ -0,0 +1,109 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Release
+ Win32
+
+
+
+ {3C558AD9-5F9C-4A14-8F07-800F46C132C7}
+ Win32Proj
+ UbiTMNTHax
+ UbiTMNTHax
+ 10.0
+
+
+
+ DynamicLibrary
+ true
+ v143
+ MultiByte
+
+
+ DynamicLibrary
+ false
+ v143
+ true
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+ true
+ .asi
+ $(ProjectName)
+
+
+ false
+ .asi
+ $(SolutionDir)$(Configuration)\$(ProjectName)\scripts\
+ $(ProjectName)
+
+
+
+ Use
+ Level3
+ Disabled
+ WIN32;_DEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+
+
+
+
+ Level4
+ Use
+ MaxSpeed
+ true
+ true
+ WIN32;NDEBUG;_WINDOWS;_USRDLL;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)
+
+
+ Windows
+ true
+ true
+ true
+
+
+
+
+
+
+
+
+ false
+
+
+ false
+
+
+
+
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/dllmain.cpp b/dllmain.cpp
new file mode 100644
index 0000000..3064020
--- /dev/null
+++ b/dllmain.cpp
@@ -0,0 +1,213 @@
+#include "stdafx.h"
+#include "stdio.h"
+#include "includes\injector\injector.hpp"
+#include "includes\IniReader.h"
+
+bool bBorderlessWindowed = false;
+bool bEnableWindowResize = false;
+bool bEnableConsole = false;
+bool bAllowMultipleInstances = false;
+bool bRelocateWindow = false;
+
+int RelocateX = 0;
+int RelocateY = 0;
+
+int DesktopX = 0;
+int DesktopY = 0;
+
+HWND GameHWND = NULL;
+tagRECT windowRect;
+
+void ReadFullscreenSetting(char* IniPath)
+{
+ // restore the config read
+ *(int*)((*(int*)0x823F60) + 0xD8) = GetPrivateProfileIntA("CONFIG", "Fullscreen", 1, IniPath);
+}
+
+int InitConfig()
+{
+ CIniReader inireader("");
+ bBorderlessWindowed = inireader.ReadInteger("TMNTHax", "BorderlessWindowed", 0);
+ // breaks mouse positioning... needs aspect ratio recalculation from 16:9
+ bEnableWindowResize = inireader.ReadInteger("TMNTHax", "EnableWindowResize", 0);
+ bEnableConsole = inireader.ReadInteger("TMNTHax", "EnableConsole", 0);
+ bAllowMultipleInstances = inireader.ReadInteger("TMNTHax", "AllowMultipleInstances", 0);
+ bRelocateWindow = inireader.ReadInteger("TMNTHax", "RelocateWindow", 0);
+
+ if (bRelocateWindow)
+ {
+ RelocateX = inireader.ReadInteger("WindowLocation", "X", 0);
+ RelocateY = inireader.ReadInteger("WindowLocation", "Y", 0);
+ }
+
+ return 0;
+}
+
+int __stdcall cave_sprintf(char* buf, const char* Format, ...)
+{
+ va_list ArgList;
+ int Result = 0;
+
+ __crt_va_start(ArgList, Format);
+ Result = vsprintf(buf, Format, ArgList);
+ __crt_va_end(ArgList);
+
+ ReadFullscreenSetting(buf);
+
+ return Result;
+}
+
+int __stdcall cave_sprintf2(char* buf, const char* Format, ...)
+{
+ va_list ArgList;
+ int Result = 0;
+
+ __crt_va_start(ArgList, Format);
+ Result = vsprintf(buf, Format, ArgList);
+ __crt_va_end(ArgList);
+
+ puts(buf);
+
+ return Result;
+}
+
+int cave_vsnprintf(char* buf, const size_t bufcount, const char* Format, va_list ArgList)
+{
+ int Result = 0;
+
+ Result = vsnprintf(buf, bufcount, Format, ArgList);
+ vprintf(Format, ArgList);
+
+ return Result;
+}
+
+void __stdcall OutputDebugStringHook(LPCSTR lpOutputString)
+{
+ printf("%s", lpOutputString);
+ return OutputDebugStringA(lpOutputString);
+}
+
+int GetDesktopRes(int32_t *DesktopResW, int32_t *DesktopResH)
+{
+ HMONITOR monitor = MonitorFromWindow(GetDesktopWindow(), MONITOR_DEFAULTTONEAREST);
+ MONITORINFO info = {};
+ info.cbSize = sizeof(MONITORINFO);
+ GetMonitorInfo(monitor, &info);
+ *DesktopResW = info.rcMonitor.right - info.rcMonitor.left;
+ *DesktopResH = info.rcMonitor.bottom - info.rcMonitor.top;
+ return 0;
+}
+
+BOOL WINAPI AdjustWindowRect_Hook(LPRECT lpRect, DWORD dwStyle, BOOL bMenu)
+{
+ DWORD newStyle = 0;
+
+ if (!bBorderlessWindowed)
+ newStyle = WS_CAPTION;
+
+ return AdjustWindowRect(lpRect, newStyle, bMenu);
+}
+
+HWND WINAPI CreateWindowExA_Hook(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam)
+{
+ int WindowPosX = RelocateX;
+ int WindowPosY = RelocateY;
+
+ GetDesktopRes(&DesktopX, &DesktopY);
+
+ if (!bRelocateWindow)
+ {
+ // fix the window to open at the center of the screen...
+ WindowPosX = (int)(((float)DesktopX / 2.0f) - ((float)nWidth / 2.0f));
+ WindowPosY = (int)(((float)DesktopY / 2.0f) - ((float)nHeight / 2.0f));
+ }
+
+ GameHWND = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, 0, WindowPosX, WindowPosY, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam);
+ LONG lStyle = GetWindowLong(GameHWND, GWL_STYLE);
+
+ if (bBorderlessWindowed)
+ lStyle &= ~(WS_CAPTION | WS_THICKFRAME | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_SYSMENU);
+ else
+ {
+ lStyle |= (WS_MINIMIZEBOX | WS_SYSMENU);
+ if (bEnableWindowResize)
+ lStyle |= (WS_MAXIMIZEBOX | WS_THICKFRAME);
+ }
+
+ SetWindowLong(GameHWND, GWL_STYLE, lStyle);
+ SetWindowPos(GameHWND, 0, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOZORDER | SWP_FRAMECHANGED);
+
+ return GameHWND;
+}
+
+BOOL WINAPI GetCursorPos_Hook(LPPOINT lpPoint)
+{
+ BOOL retval = GetCursorPos(lpPoint);
+ GetWindowRect(GameHWND, &windowRect);
+
+ // correct the mouse position on moved window... this needs a lot more detail fixing - when window is resized, this breaks
+ lpPoint->x = lpPoint->x - windowRect.left;
+ lpPoint->y = lpPoint->y - windowRect.top;
+
+ return retval;
+}
+
+bool RetTrue()
+{
+ return true;
+}
+
+int Init()
+{
+ injector::MakeCALL(0x0040B91B, cave_sprintf, true);
+
+ injector::WriteMemory(0x007A6278, (int)&CreateWindowExA_Hook, true);
+ injector::WriteMemory(0x007A627C, (int)&AdjustWindowRect_Hook, true);
+ injector::WriteMemory(0x007A62A0, (int)&GetCursorPos_Hook, true);
+
+ // console window stuff...
+ if (bEnableConsole)
+ {
+ injector::MakeCALL(0x004B65BA, cave_sprintf2, true);
+ injector::MakeCALL(0x004B61B4, cave_sprintf2, true);
+ injector::MakeCALL(0x004B61F4, cave_sprintf2, true);
+ injector::MakeCALL(0x004B654F, cave_sprintf2, true);
+ injector::MakeCALL(0x004181FD, cave_vsnprintf, true);
+ injector::WriteMemory(0x007A61E0, (int)&OutputDebugStringHook, true);
+ //injector::MakeCALL(0x0041819C, cave_vsnprintf, true);
+ }
+
+ // kill Launcher mutex creation so the game can be started directly...
+ injector::MakeJMP(0x0041507E, 0x41510B, true);
+
+ // kill instance mutex -- doesn't work for some reason... broken by Launcher mutex kill
+ if (bAllowMultipleInstances)
+ injector::MakeJMP(0x00414FAF, 0x41510B, true);
+
+ // Disable CD check...
+ injector::MakeNOP(0x0040607C, 2, true);
+
+ return 0;
+}
+
+BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD reason, LPVOID /*lpReserved*/)
+{
+ if (reason == DLL_PROCESS_ATTACH)
+ {
+ InitConfig();
+
+ if (bEnableConsole)
+ {
+ AttachConsole(ATTACH_PARENT_PROCESS);
+ AllocConsole();
+ freopen("CON", "w", stdout);
+ freopen("CON", "w", stderr);
+ freopen("CON", "r", stdin);
+ }
+
+ Init();
+ }
+ return TRUE;
+}
+
+
diff --git a/includes/CPatch.h b/includes/CPatch.h
new file mode 100644
index 0000000..05f4ee2
--- /dev/null
+++ b/includes/CPatch.h
@@ -0,0 +1,145 @@
+#pragma once
+#include
+
+class CPatch
+{
+public:
+ inline static void Patch(void* address, void* data, int size)
+ {
+ unsigned long protect[2];
+ VirtualProtect(address, size, PAGE_EXECUTE_READWRITE, &protect[0]);
+ memcpy(address, data, size);
+ VirtualProtect(address, size, protect[0], &protect[1]);
+ }
+
+ inline static void Patch2(int address, void* data, int size)
+ {
+ unsigned long protect[2];
+ VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]);
+ memcpy((void *)address, data, size);
+ VirtualProtect((void *)address, size, protect[0], &protect[1]);
+ }
+
+ inline static void Unprotect(int address, int size)
+ {
+ unsigned long protect[2];
+ VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]);
+ }
+ inline static void Nop(int address, int size)
+ {
+ unsigned long protect[2];
+ VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]);
+ memset((void *)address, 0x90, size);
+ VirtualProtect((void *)address, size, protect[0], &protect[1]);
+ }
+ inline static void FillWithZeroes(int address, int size)
+ {
+ unsigned long protect[2];
+ VirtualProtect((void *)address, size, PAGE_EXECUTE_READWRITE, &protect[0]);
+ memset((void *)address, 0x00, size);
+ VirtualProtect((void *)address, size, protect[0], &protect[1]);
+ }
+ inline static void RedirectCall(int address, void *func)
+ {
+ int temp = 0xE8;
+ Patch((void *)address, &temp, 1);
+ temp = (int)func - ((int)address + 5);
+ Patch((void *)((int)address + 1), &temp, 4);
+ }
+ inline static void RedirectJump(int address, void *func)
+ {
+ int temp = 0xE9;
+ Patch((void *)address, &temp, 1);
+ temp = (int)func - ((int)address + 5);
+ Patch((void *)((int)address + 1), &temp, 4);
+ }
+ inline static void SetChar(int address, char value)
+ {
+ Patch((void *)address, &value, 1);
+ }
+ inline static void SetUChar(int address, unsigned char value)
+ {
+ Patch((void *)address, &value, 1);
+ }
+ inline static void SetShort(int address, short value)
+ {
+ Patch((void *)address, &value, 2);
+ }
+ inline static void SetUShort(int address, unsigned short value)
+ {
+ Patch((void *)address, &value, 2);
+ }
+ inline static void SetInt(int address, int value)
+ {
+ Patch((void *)address, &value, 4);
+ }
+ inline static void SetUInt(int address, unsigned int value)
+ {
+ Patch((void *)address, &value, 4);
+ }
+ inline static void SetUIntWithCheck(int address, unsigned int value, unsigned int expectedValue)
+ {
+ if (*(unsigned int *)address == expectedValue)
+ Patch((void *)address, &value, 4);
+ }
+ inline static void SetFloat(int address, float value)
+ {
+ Patch((void *)address, &value, 4);
+ }
+ inline static void SetDouble(int address, double value)
+ {
+ Patch((void *)address, &value, 8);
+ }
+ inline static void SetPointer(int address, void *value)
+ {
+ Patch((void *)address, &value, 4);
+ }
+
+ inline static void AdjustPointer(int address, void *value, DWORD offset, DWORD end)
+ {
+ int result;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ } else {
+ address = address + 1;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ } else {
+ address = address + 1;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ } else {
+ address = address + 1;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ } else {
+ address = address + 1;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ } else {
+ address = address + 1;
+ if((DWORD)*(DWORD*)address >= offset && (DWORD)*(DWORD*)address <= end) {
+ result = (DWORD)value + (DWORD)*(DWORD*)address - (DWORD)offset;
+ Patch((void *)address, &result, 4);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ inline static bool FileExists(const TCHAR *fileName)
+ {
+ DWORD fileAttr;
+ fileAttr = GetFileAttributes(fileName);
+ if (0xFFFFFFFF == fileAttr && GetLastError()==ERROR_FILE_NOT_FOUND)
+ return false;
+ return true;
+ }
+};
\ No newline at end of file
diff --git a/includes/IniReader.h b/includes/IniReader.h
new file mode 100644
index 0000000..1ca2d4d
--- /dev/null
+++ b/includes/IniReader.h
@@ -0,0 +1,103 @@
+#ifndef INIREADER_H
+#define INIREADER_H
+
+#include "stdafx.h"
+#include
+#include
+using namespace std;
+#pragma warning(disable:4996)
+EXTERN_C IMAGE_DOS_HEADER __ImageBase;
+
+class CIniReader
+{
+public:
+ CIniReader(char* szFileName)
+ {
+ char moduleName[MAX_PATH];
+ char dllPath[MAX_PATH];
+ char iniName[MAX_PATH];
+ char* tempPointer;
+
+ GetModuleFileName((HINSTANCE)&__ImageBase, moduleName, MAX_PATH);
+ tempPointer = strrchr(moduleName, '.');
+ *tempPointer = '\0';
+ tempPointer = strrchr(moduleName, '\\');
+ strncpy(iniName, tempPointer + 1, 255);
+ strcat(iniName, ".ini");
+ strncpy(dllPath, moduleName, (tempPointer - moduleName + 1));
+ dllPath[tempPointer - moduleName + 1] = '\0';
+ if (strcmp(szFileName, "") == 0)
+ {
+ strcat(dllPath, iniName);
+ }
+ else {
+ strcat(dllPath, szFileName);
+ }
+
+ memset(m_szFileName, 0x00, 255);
+ memcpy(m_szFileName, dllPath, strlen(dllPath));
+ }
+ int ReadInteger(char* szSection, char* szKey, int iDefaultValue)
+ {
+ int iResult = GetPrivateProfileInt(szSection, szKey, iDefaultValue, m_szFileName);
+ return iResult;
+ }
+ float ReadFloat(char* szSection, char* szKey, float fltDefaultValue)
+ {
+ char szResult[255];
+ char szDefault[255];
+ float fltResult;
+ _snprintf(szDefault, 255, "%f", fltDefaultValue);
+ GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName);
+ fltResult = (float)atof(szResult);
+ return fltResult;
+ }
+ bool ReadBoolean(char* szSection, char* szKey, bool bolDefaultValue)
+ {
+ char szResult[255];
+ char szDefault[255];
+ bool bolResult;
+ _snprintf(szDefault, 255, "%s", bolDefaultValue ? "True" : "False");
+ GetPrivateProfileString(szSection, szKey, szDefault, szResult, 255, m_szFileName);
+ bolResult = (strcmp(szResult, "True") == 0 ||
+ strcmp(szResult, "true") == 0) ? true : false;
+ return bolResult;
+ }
+ char* ReadString(char* szSection, char* szKey, const char* szDefaultValue)
+ {
+ char* szResult = new char[255];
+ memset(szResult, 0x00, 255);
+ GetPrivateProfileString(szSection, szKey,
+ szDefaultValue, szResult, 255, m_szFileName);
+ return szResult;
+ }
+ void WriteInteger(char* szSection, char* szKey, int iValue)
+ {
+ char szValue[255];
+ _snprintf(szValue, 255, "%s%d", " ", iValue);
+ WritePrivateProfileString(szSection, szKey, szValue, m_szFileName);
+ }
+ void WriteFloat(char* szSection, char* szKey, float fltValue)
+ {
+ char szValue[255];
+ _snprintf(szValue, 255, "%s%f", " ", fltValue);
+ WritePrivateProfileString(szSection, szKey, szValue, m_szFileName);
+ }
+ void WriteBoolean(char* szSection, char* szKey, bool bolValue)
+ {
+ char szValue[255];
+ _snprintf(szValue, 255, "%s%s", " ", bolValue ? "True" : "False");
+ WritePrivateProfileString(szSection, szKey, szValue, m_szFileName);
+ }
+ void WriteString(char* szSection, char* szKey, char* szValue)
+ {
+ WritePrivateProfileString(szSection, szKey, szValue, m_szFileName);
+ }
+ char* GetIniPath()
+ {
+ return m_szFileName;
+ }
+private:
+ char m_szFileName[MAX_PATH];
+};
+#endif//INIREADER_H
\ No newline at end of file
diff --git a/includes/injector/LICENSE b/includes/injector/LICENSE
new file mode 100644
index 0000000..65960fb
--- /dev/null
+++ b/includes/injector/LICENSE
@@ -0,0 +1,21 @@
+Copyright (C) 2012-2014 LINK/2012
+
+This software is provided 'as-is', without any express or implied
+warranty. In no event will the authors be held liable for any damages
+arising from the use of this software.
+
+Permission is granted to anyone to use this software for any purpose,
+including commercial applications, and to alter it and redistribute it
+freely, subject to the following restrictions:
+
+ 1. The origin of this software must not be misrepresented; you must not
+ claim that you wrote the original software. If you use this software
+ in a product, an acknowledgment in the product documentation would be
+ appreciated but is not required.
+
+ 2. Altered source versions must be plainly marked as such, and must not be
+ misrepresented as being the original software.
+
+ 3. This notice may not be removed or altered from any source
+ distribution.
+
diff --git a/includes/injector/assembly.hpp b/includes/injector/assembly.hpp
new file mode 100644
index 0000000..9854a34
--- /dev/null
+++ b/includes/injector/assembly.hpp
@@ -0,0 +1,179 @@
+/*
+ * Injectors - Useful Assembly Stuff
+ *
+ * Copyright (C) 2012-2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+
+// This header is very restrict about compiler and architecture
+#ifndef _MSC_VER // MSVC is much more flexible when we're talking about inline assembly
+#error Cannot use this header in another compiler other than MSVC
+#endif
+#ifndef _M_IX86
+#error Supported only in x86
+#endif
+
+//
+#include
+#include "injector.hpp"
+
+namespace injector
+{
+ struct reg_pack
+ {
+ // The ordering is very important, don't change
+ // The first field is the last to be pushed and first to be poped
+
+ // PUSHFD / POPFD
+ uint32_t ef;
+
+ // PUSHAD/POPAD -- must be the lastest fields (because of esp)
+ union
+ {
+ uint32_t arr[8];
+ struct { uint32_t edi, esi, ebp, esp, ebx, edx, ecx, eax; };
+ };
+
+ enum reg_name {
+ reg_edi, reg_esi, reg_ebp, reg_esp, reg_ebx, reg_edx, reg_ecx, reg_eax
+ };
+
+ enum ef_flag {
+ carry_flag = 0, parity_flag = 2, adjust_flag = 4, zero_flag = 6, sign_flag = 7,
+ direction_flag = 10, overflow_flag = 11
+ };
+
+ uint32_t& operator[](size_t i)
+ { return this->arr[i]; }
+ const uint32_t& operator[](size_t i) const
+ { return this->arr[i]; }
+
+ template // bit starts from 0, use ef_flag enum
+ bool flag()
+ {
+ return (this->ef & (1 << bit)) != 0;
+ }
+
+ bool jnb()
+ {
+ return flag() == false;
+ }
+ };
+
+ // Lowest level stuff (actual assembly) goes on the following namespace
+ // PRIVATE! Skip this, not interesting for you.
+ namespace injector_asm
+ {
+ // Wrapper functor, so the assembly can use some templating
+ template
+ struct wrapper
+ {
+ static void call(reg_pack* regs)
+ {
+ T fun; fun(*regs);
+ }
+ };
+
+ // Constructs a reg_pack and calls the wrapper functor
+ template // where W is of type wrapper
+ inline void __declspec(naked) make_reg_pack_and_call()
+ {
+ _asm
+ {
+ // Construct the reg_pack structure on the stack
+ pushad // Pushes general purposes registers to reg_pack
+ add [esp+12], 4 // Add 4 to reg_pack::esp 'cuz of our return pointer, let it be as before this func is called
+ pushfd // Pushes EFLAGS to reg_pack
+
+ // Call wrapper sending reg_pack as parameter
+ push esp
+ call W::call
+ add esp, 4
+
+ // Destructs the reg_pack from the stack
+ sub [esp+12+4], 4 // Fix reg_pack::esp before popping it (doesn't make a difference though) (+4 because eflags)
+ popfd // Warning: Do not use any instruction that changes EFLAGS after this (-> sub affects EF!! <-)
+ popad
+
+ // Back to normal flow
+ ret
+ }
+ }
+ };
+
+
+ /*
+ * MakeInline
+ * Makes inline assembly (but not assembly, an actual functor of type FuncT) at address
+ */
+ template
+ void MakeInline(memory_pointer_tr at)
+ {
+ typedef injector_asm::wrapper functor;
+ if(false) functor::call(nullptr); // To instantiate the template, if not done _asm will fail
+ MakeCALL(at, injector_asm::make_reg_pack_and_call);
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but it NOPs everything between at and end (exclusive), then performs MakeInline
+ */
+ template
+ void MakeInline(memory_pointer_tr at, memory_pointer_tr end)
+ {
+ MakeRangedNOP(at, end);
+ MakeInline(at);
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but (at,end) are template parameters.
+ * On this case the functor can be passed as argument since there will be one func instance for each at,end not just for each FuncT
+ */
+ template
+ void MakeInline(FuncT func)
+ {
+ static std::unique_ptr static_func;
+ static_func.reset(new FuncT(std::move(func)));
+
+ // Encapsulates the call to static_func
+ struct Caps
+ {
+ void operator()(reg_pack& regs)
+ { (*static_func)(regs); }
+ };
+
+ // Does the actual MakeInline
+ return MakeInline(lazy_pointer::get(), lazy_pointer::get());
+ }
+
+ /*
+ * MakeInline
+ * Same as above, but (end) is calculated by the length of a call instruction
+ */
+ template
+ void MakeInline(FuncT func)
+ {
+ return MakeInline(func);
+ }
+};
diff --git a/includes/injector/calling.hpp b/includes/injector/calling.hpp
new file mode 100644
index 0000000..ebf3bdf
--- /dev/null
+++ b/includes/injector/calling.hpp
@@ -0,0 +1,126 @@
+/*
+ * Injectors - Function Calls Using Variadic Templates
+ *
+ * Copyright (C) 2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+#include "injector.hpp"
+#include
+
+#if __cplusplus >= 201103L || _MSC_VER >= 1800 // MSVC 2013
+#else
+#error "This feature is not supported on this compiler"
+#endif
+
+namespace injector
+{
+ template
+ struct cstd;
+
+ template
+ struct cstd
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(*)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct stdcall;
+
+ template
+ struct stdcall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__stdcall *)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct fastcall;
+
+ template
+ struct fastcall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__fastcall *)(Args...)) p.get();;
+ return fn(std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+
+ template
+ struct thiscall;
+
+ template
+ struct thiscall
+ {
+ // Call function at @p returning @Ret with args @Args
+ static Ret call(memory_pointer_tr p, Args... a)
+ {
+ auto fn = (Ret(__thiscall *)(Args...)) p.get();
+ return fn(std::forward(a)...);
+ }
+
+ // Call function at the index @i from the vtable of the object @a[0]
+ template
+ static Ret vtbl(Args... a)
+ {
+ auto obj = raw_ptr(std::get<0>(std::forward_as_tuple(a...)));
+ auto p = raw_ptr( (*obj.template get()) [i] );
+ return call(p, std::forward(a)...);
+ }
+
+ template // Uses lazy pointer
+ static Ret call(Args... a)
+ {
+ return call(lazy_pointer::get(), std::forward(a)...);
+ }
+ };
+}
+
diff --git a/includes/injector/gvm/gvm.hpp b/includes/injector/gvm/gvm.hpp
new file mode 100644
index 0000000..a957929
--- /dev/null
+++ b/includes/injector/gvm/gvm.hpp
@@ -0,0 +1,232 @@
+/*
+ * Injectors - Base Header
+ *
+ * Copyright (C) 2012-2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+#include
+#include
+#include
+
+namespace injector
+{
+
+#if 1 // GVM and Address Translator, Not very interesting for the users, so skip reading those...
+
+/*
+ * game_version_manager
+ * Detects the game, the game version and the game region
+ * This assumes the executable is decrypted, so, Silent's ASI Loader is recommended.
+ */
+#ifndef INJECTOR_OWN_GVM
+#ifndef INJECTOR_GVM_DUMMY
+class game_version_manager
+{
+ public:
+ // Set this if you would like that MessagesBox contain PluginName as caption
+ const char* PluginName;
+
+ private:
+ char game, region, major, minor, majorRevision, minorRevision, cracker, steam;
+
+ public:
+ game_version_manager()
+ {
+ #ifdef INJECTOR_GVM_PLUGIN_NAME
+ PluginName = INJECTOR_GVM_PLUGIN_NAME;
+ #else
+ PluginName = "LODLights.asi";
+ #endif
+
+ this->Clear();
+ }
+
+
+ // Clear any information about game version
+ void Clear()
+ {
+ game = region = major = minor = cracker = steam = 0;
+ }
+
+ // Checks if I don't know the game we are attached to
+ bool IsUnknown() { return game == 0; }
+ // Checks if this is the steam version
+ bool IsSteam() { return steam != 0; }
+ // Gets the game we are attached to (0, '3', 'V', 'S', 'I', 'E')
+ char GetGame() { return game; }
+ // Gets the region from the game we are attached to (0, 'U', 'E');
+ char GetRegion() { return region; }
+ // Get major and minor version of the game (e.g. [major = 1, minor = 0] = 1.0)
+ int GetMajorVersion() { return major; }
+ int GetMinorVersion() { return minor; }
+ int GetMajorRevisionVersion() { return majorRevision; }
+ int GetMinorRevisionVersion() { return minorRevision; }
+
+ bool IsHoodlum() { return cracker == 'H'; }
+
+ // Region conditions
+ bool IsUS() { return region == 'U'; }
+ bool IsEU() { return region == 'E'; }
+
+ // Game Conditions
+ bool IsIII() { return game == '3'; }
+ bool IsVC () { return game == 'V'; }
+ bool IsSA () { return game == 'S'; }
+ bool IsIV () { return game == 'I'; }
+ bool IsEFLC(){ return game == 'E'; }
+
+ // Detects game, region and version; returns false if could not detect it
+ bool Detect();
+
+ // Gets the game version as text, the buffer must contain at least 32 bytes of space.
+ char* GetVersionText(char* buffer)
+ {
+ if(this->IsUnknown())
+ {
+ strcpy(buffer, "UNKNOWN GAME");
+ return buffer;
+ }
+
+ const char* g = this->IsIII() ? "III" : this->IsVC() ? "VC" : this->IsSA() ? "SA" : this->IsIV() ? "IV" : this->IsEFLC() ? "EFLC" : "UNK";
+ const char* r = this->IsUS()? "US" : this->IsEU()? "EURO" : "UNK_REGION";
+ const char* s = this->IsSteam()? "Steam" : "";
+ if (this->IsIII() || this->IsVC() || this->IsSA())
+ sprintf(buffer, "GTA %s %d.%d %s%s", g, major, minor, r, s);
+ else
+ sprintf(buffer, "GTA %s %d.%d.%d.%d %s%s", g, major, minor, majorRevision, minorRevision, r, s);
+ return buffer;
+ }
+
+
+ public:
+ // Raises a error saying that you could not detect the game version
+ void RaiseCouldNotDetect()
+ {
+ MessageBoxA(0,
+ "Could not detect the game version\nContact the mod creator!",
+ PluginName, MB_ICONERROR
+ );
+ }
+
+ // Raises a error saying that the exe version is incompatible (and output the exe name)
+ void RaiseIncompatibleVersion()
+ {
+ char buf[128], v[32];
+ sprintf(buf,
+ "An incompatible exe version has been detected! (%s)\nContact the mod creator!",
+ GetVersionText(v)
+ );
+ MessageBoxA(0, buf, PluginName, MB_ICONERROR);
+ }
+};
+#else // INJECTOR_GVM_DUMMY
+class game_version_manager
+{
+ public:
+ bool Detect() { return true; }
+};
+#endif // INJECTOR_GVM_DUMMY
+#endif // INJECTOR_OWN_GVM
+
+
+/*
+ * address_manager
+ * Address translator from 1.0 executables to other executables offsets
+ * Inherits from game_version_manager ;)
+ */
+class address_manager : public game_version_manager
+{
+ private:
+ address_manager()
+ {
+ this->Detect();
+ }
+
+ // You could implement your translator for the address your plugin uses
+ // If not implemented, the translator won't translate anything, just return the samething as before
+ #ifdef INJECTOR_GVM_HAS_TRANSLATOR
+ void* translator(void* p);
+ #else
+ void* translator(void* p) { return p; }
+ #endif
+
+ public:
+ // Translates address p to the running executable pointer
+ void* translate(void* p)
+ {
+ return translator(p);
+ }
+
+
+ public:
+ // Address manager singleton
+ static address_manager& singleton()
+ {
+ static address_manager m;
+ return m;
+ }
+
+ // Static version of translate()
+ static void* translate_address(void* p)
+ {
+ return singleton().translate(p);
+ }
+
+ //
+ static void set_name(const char* modname)
+ {
+ singleton().PluginName = modname;
+ }
+
+ public:
+ // Functors for memory translation:
+
+ // Translates aslr translator
+ struct fn_mem_translator_aslr
+ {
+ void* operator()(void* p) const
+ {
+ static uintptr_t module = (uintptr_t)GetModuleHandle(NULL);
+ return (void*)((uintptr_t)(p)-(0x400000 - module));
+ }
+ };
+
+ // Translates nothing translator
+ struct fn_mem_translator_nop
+ {
+ void* operator()(void* p) const
+ { return p; }
+ };
+
+ // Real translator
+ struct fn_mem_translator
+ {
+ void* operator()(void* p) const
+ { return translate_address(p); }
+ };
+};
+
+#endif // #if 1
+
+
+}
\ No newline at end of file
diff --git a/includes/injector/gvm/translator.hpp b/includes/injector/gvm/translator.hpp
new file mode 100644
index 0000000..c6ac430
--- /dev/null
+++ b/includes/injector/gvm/translator.hpp
@@ -0,0 +1,203 @@
+/*
+ * Injectors - Address Translation Management
+ *
+ * Copyright (C) 2014 LINK/2012
+ *
+ * This software is provided 'as-is', without any express or implied
+ * warranty. In no event will the authors be held liable for any damages
+ * arising from the use of this software.
+ *
+ * Permission is granted to anyone to use this software for any purpose,
+ * including commercial applications, and to alter it and redistribute it
+ * freely, subject to the following restrictions:
+ *
+ * 1. The origin of this software must not be misrepresented; you must not
+ * claim that you wrote the original software. If you use this software
+ * in a product, an acknowledgment in the product documentation would be
+ * appreciated but is not required.
+ *
+ * 2. Altered source versions must be plainly marked as such, and must not be
+ * misrepresented as being the original software.
+ *
+ * 3. This notice may not be removed or altered from any source
+ * distribution.
+ *
+ */
+#pragma once
+
+#if !defined(INJECTOR_GVM_HAS_TRANSLATOR)
+#error Missing INJECTOR_GVM_HAS_TRANSLATOR on compiler definitions
+#endif
+
+/*
+ * This is a quick solution for address translations if you're too lazy to implement a proper address_manager::translator by yourself
+ * So, just call address_translator_manager::singleton().translate(p) from your address_manager::translator and that's it.
+ * It'll translate addresses based on 'address_translator' objects, when one gets constructed it turns into a possible translator.
+ * At the constructor of your derived 'address_translator' make the map object to have [addr_to_translate] = translated_addr;
+ * There's also the virtual method 'fallback' that will get called when the translation wasn't possible, you can do some fallback stuff here
+ * (such as return the pointer as is or output a error message)
+ */
+
+#include "../injector.hpp"
+#include
+#include