Skip to content

Commit

Permalink
plugin source
Browse files Browse the repository at this point in the history
  • Loading branch information
ThirteenAG committed Aug 21, 2019
1 parent 9201daa commit 0229d83
Show file tree
Hide file tree
Showing 14 changed files with 552 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ bld/
[Bb]in/
[Oo]bj/
[Ll]og/
[Bb]uild/

# Visual Studio 2015/2017 cache/options directory
.vs/
Expand Down
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "external/injector"]
path = external/injector
url = https://github.com/thelink2012/injector
[submodule "external/hooking"]
path = external/hooking
url = https://github.com/ThirteenAG/Hooking.Patterns
[submodule "external/inireader"]
path = external/inireader
url = https://github.com/ThirteenAG/IniReader
1 change: 1 addition & 0 deletions external/hooking
Submodule hooking added at 542b8d
1 change: 1 addition & 0 deletions external/inireader
Submodule inireader added at 85c284
1 change: 1 addition & 0 deletions external/injector
Submodule injector added at 07e6a5
1 change: 1 addition & 0 deletions premake5.bat
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
premake5 vs2019
Binary file added premake5.exe
Binary file not shown.
66 changes: 66 additions & 0 deletions premake5.lua
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
workspace "GTAIV.EFLC.FusionFix"
configurations { "Release", "Debug" }
architecture "x86"
location "build"
buildoptions {"-std:c++latest"}
kind "SharedLib"
language "C++"
targetdir "bin/%{cfg.buildcfg}"
targetextension ".asi"

defines { "rsc_CompanyName=\"GTAIV.EFLC.FusionFix\"" }
defines { "rsc_LegalCopyright=\"GTAIV.EFLC.FusionFix\""}
defines { "rsc_FileVersion=\"1.0.0.0\"", "rsc_ProductVersion=\"1.0.0.0\"" }
defines { "rsc_InternalName=\"%{prj.name}\"", "rsc_ProductName=\"%{prj.name}\"", "rsc_OriginalFilename=\"%{prj.name}.dll\"" }
defines { "rsc_FileDescription=\"GTAIV.EFLC.FusionFix\"" }
defines { "rsc_UpdateUrl=\"https://github.com/ThirteenAG/GTAIV.EFLC.FusionFix\"" }

includedirs { "source" }
files { "source/dllmain.cpp" }
files { "source/resources/Versioninfo.rc" }

includedirs { "external/hooking" }
includedirs { "external/injector/include" }
includedirs { "external/inireader" }
files { "external/hooking/Hooking.Patterns.h", "external/hooking/Hooking.Patterns.cpp" }

characterset ("Unicode")

pbcommands = {
"setlocal EnableDelayedExpansion",
--"set \"path=" .. (gamepath) .. "\"",
"set file=$(TargetPath)",
"FOR %%i IN (\"%file%\") DO (",
"set filename=%%~ni",
"set fileextension=%%~xi",
"set target=!path!!filename!!fileextension!",
"if exist \"!target!\" copy /y \"!file!\" \"!target!\"",
")" }

function setpaths (gamepath, exepath, scriptspath)
scriptspath = scriptspath or "scripts/"
if (gamepath) then
cmdcopy = { "set \"path=" .. gamepath .. scriptspath .. "\"" }
table.insert(cmdcopy, pbcommands)
postbuildcommands (cmdcopy)
debugdir (gamepath)
if (exepath) then
debugcommand (gamepath .. exepath)
dir, file = exepath:match'(.*/)(.*)'
debugdir (gamepath .. (dir or ""))
end
end
targetdir ("bin")
end

filter "configurations:Debug"
defines { "DEBUG" }
symbols "On"

filter "configurations:Release"
defines { "NDEBUG" }
optimize "On"
staticruntime "On"

project "GTAIV.EFLC.FusionFix"
setpaths("G:/Games/Grand Theft Auto IV v1.0.8.0 - DLC/", "GTAIV.exe", "plugins/")
185 changes: 185 additions & 0 deletions source/ModuleList.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
#pragma once

#include <vector>
#include <algorithm>
#include <cassert>
#include <tuple>

// Stores a list of loaded modules with their names, WITHOUT extension
class ModuleList
{
public:
enum class SearchLocation
{
All,
LocalOnly,
SystemOnly,
};

// Initializes module list
// Needs to be called before any calls to Get or GetAll
void Enumerate(SearchLocation location = SearchLocation::All)
{
constexpr size_t INITIAL_SIZE = sizeof(HMODULE) * 256;
HMODULE* modules = static_cast<HMODULE*>(malloc(INITIAL_SIZE));
if (modules != nullptr)
{
typedef BOOL(WINAPI * Func)(HANDLE hProcess, HMODULE *lphModule, DWORD cb, LPDWORD lpcbNeeded);

HMODULE hLib = LoadLibrary(TEXT("kernel32"));
assert(hLib != nullptr); // If this fails then everything is probably broken anyway

Func pEnumProcessModules = reinterpret_cast<Func>(GetProcAddress(hLib, "K32EnumProcessModules"));
if (pEnumProcessModules == nullptr)
{
// Try psapi
FreeLibrary(hLib);
hLib = LoadLibrary(TEXT("psapi"));
if (hLib != nullptr)
{
pEnumProcessModules = reinterpret_cast<Func>(GetProcAddress(hLib, "EnumProcessModules"));
}
}

if (pEnumProcessModules != nullptr)
{
const HANDLE currentProcess = GetCurrentProcess();
DWORD cbNeeded = 0;
if (pEnumProcessModules(currentProcess, modules, INITIAL_SIZE, &cbNeeded) != 0)
{
if (cbNeeded > INITIAL_SIZE)
{
HMODULE* newModules = static_cast<HMODULE*>(realloc(modules, cbNeeded));
if (newModules != nullptr)
{
modules = newModules;

if (pEnumProcessModules(currentProcess, modules, cbNeeded, &cbNeeded) != 0)
{
EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE));
}
}
}
else
{
EnumerateInternal(modules, location, cbNeeded / sizeof(HMODULE));
}
}
}

if (hLib != nullptr)
{
FreeLibrary(hLib);
}

free(modules);
}
}

// Recreates module list
void ReEnumerate(SearchLocation location = SearchLocation::All)
{
Clear();
Enumerate(location);
}

// Clears module list
void Clear()
{
m_moduleList.clear();
}

// Gets handle of a loaded module with given name, NULL otherwise
HMODULE Get(const wchar_t* moduleName) const
{
// If vector is empty then we're trying to call it without calling Enumerate first
assert(m_moduleList.size() != 0);

auto it = std::find_if(m_moduleList.begin(), m_moduleList.end(), [&](const auto& e) {
return _wcsicmp(moduleName, std::get<1>(e).c_str()) == 0;
});
return it != m_moduleList.end() ? std::get<0>(*it) : nullptr;
}

// Gets handles to all loaded modules with given name
std::vector<HMODULE> GetAll(const wchar_t* moduleName) const
{
// If vector is empty then we're trying to call it without calling Enumerate first
assert(m_moduleList.size() != 0);

std::vector<HMODULE> results;
for (auto& e : m_moduleList)
{
if (_wcsicmp(moduleName, std::get<1>(e).c_str()) == 0)
{
results.push_back(std::get<0>(e));
}
}

return results;
}

private:
void EnumerateInternal(HMODULE* modules, SearchLocation location, size_t numModules)
{
auto GetModuleFileNameW = [](HMODULE hModule) -> std::wstring
{
static constexpr auto INITIAL_BUFFER_SIZE = MAX_PATH;
static constexpr auto MAX_ITERATIONS = 7;
std::wstring ret;
auto bufferSize = INITIAL_BUFFER_SIZE;
for (size_t iterations = 0; iterations < MAX_ITERATIONS; ++iterations)
{
ret.resize(bufferSize);
auto charsReturned = ::GetModuleFileNameW(hModule, &ret[0], bufferSize);
if (charsReturned < ret.length())
{
ret.resize(charsReturned);
return ret;
}
else
{
bufferSize *= 2;
}
}
return L"";
};

const auto exeModulePath = GetModuleFileNameW(NULL).substr(0, GetModuleFileNameW(NULL).find_last_of(L"/\\"));

m_moduleList.reserve(numModules);
for (size_t i = 0; i < numModules; i++)
{
// Obtain module name, with resizing if necessary
auto moduleName = GetModuleFileNameW(*modules);

if (!moduleName.empty())
{
auto starts_with = [](const std::wstring &big_str, const std::wstring &small_str) -> bool
{
return big_str.compare(0, small_str.length(), small_str) == 0;
};

const wchar_t* nameBegin = wcsrchr(moduleName.c_str(), '\\') + 1;
const wchar_t* dotPos = wcsrchr(nameBegin, '.');
bool isLocal = starts_with(std::wstring(moduleName), exeModulePath);

if ((isLocal && location != SearchLocation::SystemOnly) || (!isLocal && location != SearchLocation::LocalOnly))
{
if (dotPos != nullptr)
{
m_moduleList.emplace_back(*modules, std::wstring(nameBegin, dotPos), isLocal);
}
else
{
m_moduleList.emplace_back(*modules, nameBegin, isLocal);
}
}
}

modules++;
}
}

public: std::vector< std::tuple<HMODULE, std::wstring, bool> > m_moduleList;
};
70 changes: 70 additions & 0 deletions source/dllmain.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
#include <misc.h>

void Init()
{
//recoil fix
static float fRecMult = 0.65f;
auto pattern = hook::pattern("F3 0F 10 44 24 ? F3 0F 59 05 ? ? ? ? EB ? E8");
injector::WriteMemory(pattern.get_first(10), &fRecMult, true);

//disable forced definition-off in cutscenes
pattern = hook::pattern("E8 ? ? ? ? F6 D8 1A C0 F6 D0 22 05 ? ? ? ? C3"); //0x88CC6B
injector::WriteMemory<uint16_t>(pattern.get_first(11), 0xA090, true); //mov al ...

// workaround for gta_emissivestrong.fxc lights on patch 6+,
//"A0 01 00 00 02 00 00 08" replaced in shader files with "A1 01 00 00 02 00 00 08" (5 occurrences)
pattern = hook::pattern("C1 E7 04 C1 E3 04 8B C7 83 F8 04 8D 9B ? ? ? ? 8B CB 72 14 8B 32 3B 31 75 12 83 E8 04");
struct ShaderTest
{
void operator()(injector::reg_pack& regs)
{
if (
*(uint32_t*)(regs.edx + 0x00) == 0x39D1B717 &&
*(uint32_t*)(regs.edx + 0x10) == 0x41000000 && //traffic lights and lamps
*(uint32_t*)(regs.edx - 0x10) == 0x3B03126F &&
*(uint32_t*)(regs.edx + 0x20) == 0x3E59999A &&
*(uint32_t*)(regs.edx + 0x24) == 0x3F372474 &&
*(uint32_t*)(regs.edx + 0x28) == 0x3D93A92A
)
{
*(float*)(regs.edx + 0x00) = -*(float*)(regs.edx + 0x00);
}

regs.edi = regs.edi << 4;
regs.ebx = regs.ebx << 4;
}
}; injector::MakeInline<ShaderTest>(pattern.count(2).get(1).get<void>(0), pattern.count(2).get(1).get<void>(6)); //417800

//pattern = hook::pattern("0F B6 29 0F B6 32 2B F5 75 45 83 E8 01 83 C1 01 83 C2 01 85 C0");
//struct ShaderTest2
//{
// void operator()(injector::reg_pack& regs)
// {
// *(uint8_t*)&regs.ebp = *(uint8_t*)(regs.ecx + 0x00);
// *(uint8_t*)&regs.esi = *(uint8_t*)(regs.edx + 0x00);
// }
//}; injector::MakeInline<ShaderTest2>(pattern.count(6).get(3).get<void>(0), pattern.count(6).get(3).get<void>(6)); //41782D

//draw distance adjuster
//static float f_dist_mult = 10.0f;
//pattern = hook::pattern("F3 0F 59 05 ? ? ? ? F3 0F 11 05 ? ? ? ? F3 0F 10 86"); //0x95F8AA
//injector::WriteMemory(pattern.get_first(4), &f_dist_mult, true);
//injector::WriteMemory(pattern.get_first(-12), &f_dist_mult, true);
}

CEXP void InitializeASI()
{
std::call_once(CallbackHandler::flag, []()
{
CallbackHandler::RegisterCallback(Init, hook::pattern("F3 0F 10 44 24 ? F3 0F 59 05 ? ? ? ? EB ? E8"));
});
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID lpReserved)
{
if (reason == DLL_PROCESS_ATTACH)
{
if (!IsUALPresent()) { InitializeASI(); }
}
return TRUE;
}
Loading

0 comments on commit 0229d83

Please sign in to comment.