forked from ThirteenAG/GTAIV.EFLC.FusionFix
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
9201daa
commit 0229d83
Showing
14 changed files
with
552 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
# Auto detect text files and perform LF normalization | ||
* text=auto |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ bld/ | |
[Bb]in/ | ||
[Oo]bj/ | ||
[Ll]og/ | ||
[Bb]uild/ | ||
|
||
# Visual Studio 2015/2017 cache/options directory | ||
.vs/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
premake5 vs2019 |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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*)®s.ebp = *(uint8_t*)(regs.ecx + 0x00); | ||
// *(uint8_t*)®s.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; | ||
} |
Oops, something went wrong.