mirror of
https://github.com/kurikomoe/L4D2Fix.git
synced 2026-10-10 04:43:07 +08:00
add kpatch.ini
This commit is contained in:
+26
-1
@@ -10,4 +10,29 @@
|
||||
|
||||
由于动态修改代码,请以 -insecure 启动,或者自觉不要进有 vac 的服务器(被 vac 后果自负)
|
||||
|
||||
4gb_patch.exe 用于让 left4dead2.exe 最多只能用 2G 内存提高到 4G (32 位系统下内存总计 4G,默认情况下应用只被允许用 2G,剩下的分配给系统)
|
||||
4gb_patch.exe 用于让 left4dead2.exe 最多只能用 2G 内存提高到 4G (32 位系统下内存总计 4G,默认情况下应用只被允许用 2G,剩下的分配给系统)
|
||||
|
||||
|
||||
配置文件说明:
|
||||
|
||||
[System]
|
||||
debug = false ; 开启调试输出
|
||||
|
||||
; Too Many Indices 的上限数值(buffer 大小+ 判断)
|
||||
[Indices]
|
||||
enable = true ; 修改too many indices 上限和对应的判断检测。
|
||||
value = 131072
|
||||
|
||||
|
||||
; dynamic vertex buffer 大小限制(这个修改 buffer 大小)
|
||||
[DynamicVB]
|
||||
enable = false ; 修改 dynamic vertex buffer 的大小。
|
||||
value = 65536
|
||||
|
||||
; dynamic vertex buffer 判断限制(这个只修改上限判断)
|
||||
[GetMaxToRender]
|
||||
enable = false ; 修改 dynamic vertex buffer 的上限(一般用于判断)。
|
||||
debug = true
|
||||
old_value = 32768 ; 映射机制,这里是原值。
|
||||
new_value = 65536 ; 这里是修改值
|
||||
|
||||
|
||||
@@ -13,12 +13,12 @@ clean:
|
||||
rm -rf build
|
||||
|
||||
build:
|
||||
xmake f -m testing
|
||||
xmake f -m release
|
||||
xmake
|
||||
|
||||
copy: build
|
||||
cp build/windows/x86/testing/kpatch.dll "{{TARGET}}"
|
||||
cp build/windows/x86/testing/left4dead2_fix.exe "{{TARGET}}"
|
||||
cp build/windows/x86/release/kpatch.dll "{{TARGET}}"
|
||||
cp build/windows/x86/release/left4dead2_fix.exe "{{TARGET}}"
|
||||
|
||||
copyrel:
|
||||
xmake f -m release
|
||||
@@ -28,7 +28,7 @@ copyrel:
|
||||
|
||||
release:
|
||||
rm release -rf
|
||||
mkdir -p release/testing
|
||||
mkdir -p release
|
||||
|
||||
# release
|
||||
xmake f -m release
|
||||
@@ -37,14 +37,7 @@ release:
|
||||
cp build/windows/x86/release/left4dead2_fix.exe release/
|
||||
cp assets/请读我.txt release/
|
||||
cp third/4gb_patch.exe release/
|
||||
|
||||
# testing
|
||||
xmake f -m release.testing
|
||||
xmake
|
||||
cp build/windows/x86/release.testing/kpatch.dll release/testing
|
||||
cp build/windows/x86/release.testing/left4dead2_fix.exe release/testing
|
||||
cp assets/请读我-自测补丁.txt release/testing
|
||||
cp third/4gb_patch.exe release/testing
|
||||
cp kpatch.ini release/
|
||||
|
||||
@run:
|
||||
./build/{{preset}}/bin/{{target}}
|
||||
|
||||
+21
@@ -0,0 +1,21 @@
|
||||
[System]
|
||||
debug = false
|
||||
|
||||
; Too Many Indices 的上限数值(buffer 大小+ 判断)
|
||||
[Indices]
|
||||
enable = true
|
||||
value = 131072
|
||||
|
||||
|
||||
; dynamic vertex buffer 大小限制(这个修改 buffer 大小)
|
||||
[DynamicVB]
|
||||
enable = false
|
||||
value = 65536
|
||||
|
||||
; dynamic vertex buffer 判断限制(这个只修改上限判断)
|
||||
[GetMaxToRender]
|
||||
enable = false
|
||||
debug = true
|
||||
old_value = 32768
|
||||
new_value = 65536
|
||||
|
||||
@@ -27,12 +27,6 @@ int WINAPI wWinMain(
|
||||
|
||||
LPCWSTR target_exe_path = game_name;
|
||||
|
||||
// if (std::filesystem::exists(L"left4dead2.exe.orig")) {
|
||||
// target_exe_path = L"left4dead2.exe.orig";
|
||||
// } else {
|
||||
// target_exe_path = game_name;
|
||||
// }
|
||||
|
||||
STARTUPINFOW si;
|
||||
PROCESS_INFORMATION pi;
|
||||
|
||||
|
||||
+17
-221
@@ -3,48 +3,26 @@
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <libloaderapi.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <iostream>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
|
||||
#include <algorithm>
|
||||
#include <detours/detours.h>
|
||||
#include <MinHook.h>
|
||||
#include <inipp.h>
|
||||
|
||||
#include "helper.hpp"
|
||||
|
||||
#ifdef NDEBUG
|
||||
constexpr bool isDebug = false;
|
||||
#else
|
||||
constexpr bool isDebug = true;
|
||||
#endif
|
||||
#include "vars.h"
|
||||
|
||||
#ifdef NTESTING
|
||||
constexpr bool isTesting = false;
|
||||
constexpr int VERTEX_BUFFER_SIZE = 0x020000;
|
||||
#else
|
||||
constexpr bool isTesting = true;
|
||||
constexpr int VERTEX_BUFFER_SIZE = 32;
|
||||
#endif
|
||||
#include "hooks_indices.h"
|
||||
#include "hooks_dvb.h"
|
||||
|
||||
|
||||
HMODULE baseModule = GetModuleHandle(NULL);
|
||||
HMODULE thisModule; // Fix DLL
|
||||
|
||||
// Version
|
||||
std::string sFixName = "L4D2Fix";
|
||||
std::string sFixVer = "1.3.0";
|
||||
std::string sLogFile = sFixName + ".log";
|
||||
|
||||
// Logger
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
std::filesystem::path sExePath;
|
||||
std::string sExeName;
|
||||
std::filesystem::path sThisModulePath;
|
||||
|
||||
void InitConsole() {
|
||||
AllocConsole();
|
||||
FILE *dummy;
|
||||
@@ -52,7 +30,7 @@ void InitConsole() {
|
||||
}
|
||||
|
||||
void Logging() {
|
||||
if constexpr (isDebug) {
|
||||
if (cfg::System::debug) {
|
||||
InitConsole();
|
||||
}
|
||||
// Get this module path
|
||||
@@ -75,7 +53,7 @@ void Logging() {
|
||||
|
||||
auto start_time = std::chrono::system_clock::now();
|
||||
|
||||
if (isDebug) {
|
||||
if (cfg::System::debug) {
|
||||
spdlog::set_level(spdlog::level::debug);
|
||||
}
|
||||
|
||||
@@ -99,132 +77,12 @@ void Logging() {
|
||||
}
|
||||
|
||||
|
||||
using CVertexBufferCtorFn = void* (__thiscall*)(void*, int, int, int, int, int, int, char*, char, char);
|
||||
CVertexBufferCtorFn oCVertexBufferCtorFn = nullptr;
|
||||
CVertexBufferCtorFn tCVertexBufferCtorFn = nullptr;
|
||||
intptr_t nCVertexBufferCtorFn = NULL;
|
||||
|
||||
void*__fastcall hCVertexBufferCtorFn(
|
||||
void* This,
|
||||
DWORD EDX,
|
||||
int a2,
|
||||
int a3,
|
||||
int a4,
|
||||
int a5,
|
||||
int count,
|
||||
int bufSize,
|
||||
char * source,
|
||||
char a6,
|
||||
char a7)
|
||||
{
|
||||
// std::string msg = std::format(
|
||||
// "This: {:x}, EDX: {:x}, a2: {:x}, a3: {}, a4: {}, a5: {}, count: {}, bufSize: {}, source: {}, a6: {}, a7: {}",
|
||||
// (intptr_t)This,
|
||||
// EDX,
|
||||
// a2, a3, a4, a5,
|
||||
// count, bufSize,
|
||||
// source, (int)a6, (int)a7
|
||||
// );
|
||||
// spdlog::info(msg);
|
||||
// MessageBoxW(NULL, L"inside CVertexBufferCtorFn", L"", MB_OK);
|
||||
|
||||
if (isDebug) {
|
||||
return tCVertexBufferCtorFn(This, a2, a3, a4, a5,
|
||||
count,
|
||||
32,
|
||||
source, a6, a7);
|
||||
}
|
||||
|
||||
return tCVertexBufferCtorFn(This, a2, a3, a4, a5,
|
||||
std::max(1, count * bufSize / VERTEX_BUFFER_SIZE),
|
||||
VERTEX_BUFFER_SIZE,
|
||||
source, a6, a7);
|
||||
}
|
||||
|
||||
void __declspec(naked) nakedCVertexBufferCtorFn() {
|
||||
__asm {
|
||||
call hCVertexBufferCtorFn
|
||||
jmp nCVertexBufferCtorFn
|
||||
};
|
||||
}
|
||||
|
||||
using GetMaxToRenderFn = int (__thiscall*)(void*, DWORD*, char, int*, int*);
|
||||
GetMaxToRenderFn oGetMaxToRender = nullptr;
|
||||
|
||||
int __fastcall hGetMaxToRender(
|
||||
void* This,
|
||||
DWORD EDX,
|
||||
DWORD* a2,
|
||||
char a3,
|
||||
int* p_bufsize,
|
||||
int* p_nCount
|
||||
) {
|
||||
auto ret = oGetMaxToRender(This, a2, a3, p_bufsize, p_nCount);
|
||||
// spdlog::debug("p_bufSize: {} {:x}, p_nCount: {} {:x}", *p_bufsize, *p_bufsize, *p_nCount, *p_nCount);
|
||||
if (*p_bufsize == 32768) *p_bufsize = VERTEX_BUFFER_SIZE;
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
bool fixDynamicVB(HMODULE hDll) {
|
||||
const char* pat = "0F B6 C8 8B 45 10 51 8B 4D FC 99 68 ?? ?? ?? ?? 68 00 80 00 00";
|
||||
size_t offset_imm = 0xe13b - 0xe12a;
|
||||
size_t offset_call = 0xe152 - 0xe12a;
|
||||
std::uint8_t* ptr = Memory::PatternScan(hDll, pat, 0);
|
||||
if (ptr == nullptr) {
|
||||
spdlog::info(L"Can not find the DynamicVB call");
|
||||
return false;
|
||||
}
|
||||
spdlog::info(L"Hook DynamicVB Call success");
|
||||
|
||||
ptr += offset_call;
|
||||
spdlog::info(L"Modified the DynamicVB call @ 0x{:0x}", (intptr_t)ptr - (intptr_t)hDll);
|
||||
oCVertexBufferCtorFn = reinterpret_cast<CVertexBufferCtorFn>(ptr);
|
||||
nCVertexBufferCtorFn = reinterpret_cast<intptr_t>(ptr) + 0x5;
|
||||
|
||||
// copy the target address, call is followed by an relative address
|
||||
const char* pat_GameCVertexBufferCtor = "55 8B EC 81 EC 08 01 00 00 A1 ?? ?? ?? ?? 33 C5 89 45 FC 53 56 57 8B 45 08 8B 55 10";
|
||||
std::uint8_t* ptr_GameCVertexBufferCtor = Memory::PatternScan(hDll, pat_GameCVertexBufferCtor, 0);
|
||||
if (ptr_GameCVertexBufferCtor == nullptr) {
|
||||
spdlog::info(L"Can not find the GameCVertexBufferCtor");
|
||||
return false;
|
||||
}
|
||||
tCVertexBufferCtorFn = reinterpret_cast<CVertexBufferCtorFn>(ptr_GameCVertexBufferCtor);
|
||||
spdlog::info(L"Hook GameCVertexBufferCtor success");
|
||||
|
||||
const char* pat_GetMaxToRender = "55 8B EC 57 8B 7D 08 85 FF 75 ?? 8B 45 10 89 38";
|
||||
std::uint8_t* ptr_GetMaxToRender = Memory::PatternScan(hDll, pat_GetMaxToRender, 0);
|
||||
oGetMaxToRender = reinterpret_cast<GetMaxToRenderFn>(ptr_GetMaxToRender);
|
||||
if (oGetMaxToRender == nullptr) {
|
||||
spdlog::info(L"Can not find the GetMaxToRender");
|
||||
return false;
|
||||
}
|
||||
spdlog::info(L"Hook GetMaxToRender success");
|
||||
|
||||
// Only for debugging
|
||||
// Memory::Write((uintptr_t)hDll + 0xbb67, (uint16_t)0x9090);
|
||||
|
||||
DetourRestoreAfterWith();
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread(GetCurrentThread());
|
||||
|
||||
DetourAttach(&(PVOID&)oCVertexBufferCtorFn, nakedCVertexBufferCtorFn);
|
||||
|
||||
DetourAttach(&(PVOID&)oGetMaxToRender, hGetMaxToRender);
|
||||
|
||||
DetourTransactionCommit();
|
||||
|
||||
spdlog::info(L"New DynamicVB call to 0x{:0x}", (intptr_t)oCVertexBufferCtorFn);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
DWORD __stdcall Main(void*) {
|
||||
LoadIni();
|
||||
Logging();
|
||||
|
||||
if constexpr (isTesting) {
|
||||
MessageBoxW(NULL, L"警告:这是测试版补丁,设定为必定会炸,用于检测补丁是否正确应用。", L"L4D2 Fix", MB_OK|MB_SYSTEMMODAL|MB_ICONWARNING);
|
||||
if (cfg::System::debug) {
|
||||
MessageBoxW(NULL, L"警告:你已开启 debug 输出。", L"L4D2 Fix", MB_OK|MB_SYSTEMMODAL|MB_ICONWARNING);
|
||||
}
|
||||
|
||||
std::filesystem::path startup_check = L"success.txt";
|
||||
@@ -248,6 +106,8 @@ DWORD __stdcall Main(void*) {
|
||||
spdlog::info(L"Detected Vulkan: using {}", dllName);
|
||||
isVulkan = true;
|
||||
} else {
|
||||
// Preload d3d9.dll
|
||||
LoadLibraryW(L"d3d9.dll");
|
||||
spdlog::info(L"Detected DirectX9: using {}", dllName);
|
||||
}
|
||||
|
||||
@@ -272,84 +132,20 @@ DWORD __stdcall Main(void*) {
|
||||
exit(-1);
|
||||
}
|
||||
|
||||
// Tag, pattern, start_search_path, write_offset
|
||||
auto f = [&](const wchar_t* tag, const char* pattern, size_t offset, size_t w_offset) {
|
||||
std::uint8_t* ptr = Memory::PatternScan(hDll, pattern, offset);
|
||||
|
||||
uint32_t new_buffer_size = VERTEX_BUFFER_SIZE;
|
||||
// uint32_t new_buffer_size = 0x020000;
|
||||
|
||||
if (ptr != nullptr) {
|
||||
Memory::Write((uintptr_t)(ptr + w_offset), new_buffer_size);
|
||||
spdlog::info(L"Modified the vertex max buffer {}: {} @ 0x{:0x}", dllName, tag, (intptr_t)(ptr) - (intptr_t)hDll);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
spdlog::error(L"Pattern scan failed for {}: {}", dllName, tag);
|
||||
return (uint8_t*)nullptr;
|
||||
};
|
||||
|
||||
|
||||
std::vector<void*> ptr_cache;
|
||||
std::string pattern;
|
||||
// CreateEmptyColorMesh
|
||||
|
||||
pattern = "68 ?? ?? ?? ?? 68 00 80 00 00 6A 04";
|
||||
auto* ptr1 = f(
|
||||
L"CreateEmptyColorMesh",
|
||||
pattern.c_str(),
|
||||
0,
|
||||
6
|
||||
);
|
||||
ptr_cache.push_back(ptr1);
|
||||
|
||||
pattern = "68 00 80 00 00 50 8B";
|
||||
auto* ptr1_1 = f(
|
||||
L"CreateEmptyColorMesh Later",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr1,
|
||||
1
|
||||
);
|
||||
ptr_cache.push_back(ptr1_1);
|
||||
|
||||
// CreateVertexIDBuffer
|
||||
pattern = "68 ?? ?? ?? ?? 68 00 80 00 00 6A 04";
|
||||
auto* ptr2 = f(
|
||||
L"CreateVertexIDBuffer",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr1_1,
|
||||
6
|
||||
);
|
||||
ptr_cache.push_back(ptr2);
|
||||
|
||||
pattern = "68 00 80 00 00 50 8B";
|
||||
auto* ptr2_1 = f(
|
||||
L"CreateVertexIDBuffer Later",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr2,
|
||||
1
|
||||
);
|
||||
ptr_cache.push_back(ptr2_1);
|
||||
|
||||
auto* ptr3 = f(
|
||||
L"Unknown",
|
||||
"0F B6 C0 50 68 00 80 00 00 53 8B CF E8 ?? ?? ?? ??",
|
||||
(size_t)ptr2,
|
||||
5
|
||||
);
|
||||
ptr_cache.push_back(ptr3);
|
||||
// Not known
|
||||
|
||||
auto ret = fixDynamicVB(hDll);
|
||||
auto ret = 0;
|
||||
ret += PatchIndices(hDll, dllName);
|
||||
ret += hooks_dvb(hDll, dllName);
|
||||
|
||||
if (!ret || std::ranges::any_of(ptr_cache, [](void* ptr) { return ptr == nullptr; })) {
|
||||
if (ret != 0) {
|
||||
MessageBoxW(NULL, L"未能正常应用补丁,patch 未生效,请联系开发者\n这只是一个警告,可能不影响游戏运行", L"L4D2 Fix", MB_OK);
|
||||
} else {
|
||||
std::fstream file(startup_check, std::ios::out);
|
||||
file << "补丁已生效,关闭启动提示,删除本文件可以重新启用" << std::endl;
|
||||
}
|
||||
|
||||
return true;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) {
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include <cassert>
|
||||
#include <windows.h>
|
||||
#include <cstdint>
|
||||
|
||||
+167
@@ -0,0 +1,167 @@
|
||||
#pragma once
|
||||
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <iostream>
|
||||
#include <format>
|
||||
#include <string>
|
||||
|
||||
#include <detours/detours.h>
|
||||
|
||||
#include "inipp.h"
|
||||
|
||||
#include "helper.hpp"
|
||||
#include "vars.h"
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
using CVertexBufferCtorFn = void* (__thiscall*)(void*, int, int, int, int, int, int, char*, char, char);
|
||||
CVertexBufferCtorFn oCVertexBufferCtorFn = nullptr;
|
||||
CVertexBufferCtorFn tCVertexBufferCtorFn = nullptr;
|
||||
intptr_t nCVertexBufferCtorFn = NULL;
|
||||
|
||||
void*__fastcall hCVertexBufferCtorFn(
|
||||
void* This, DWORD EDX,
|
||||
int a2, int a3, int a4, int a5,
|
||||
int count, int bufSize,
|
||||
char * source, char a6, char a7)
|
||||
{
|
||||
// std::string msg = std::format(
|
||||
// "This: {:x}, EDX: {:x}, a2: {:x}, a3: {}, a4: {}, a5: {}, count: {}, bufSize: {}, source: {}, a6: {}, a7: {}",
|
||||
// (intptr_t)This,
|
||||
// EDX,
|
||||
// a2, a3, a4, a5,
|
||||
// count, bufSize,
|
||||
// source, (int)a6, (int)a7
|
||||
// );
|
||||
// spdlog::info(msg);
|
||||
// MessageBoxW(NULL, L"inside CVertexBufferCtorFn", L"", MB_OK);
|
||||
// auto new_count = std::max(1, count * bufSize / VBS);
|
||||
auto new_count = count;
|
||||
auto new_bufsize = cfg::DynamicVB::value;
|
||||
|
||||
if (cfg::DynamicVB::debug) {
|
||||
spdlog::debug("alloc: {}, {}", new_count, new_bufsize);
|
||||
}
|
||||
|
||||
return tCVertexBufferCtorFn(This, a2, a3, a4, a5,
|
||||
new_count,
|
||||
new_bufsize,
|
||||
source, a6, a7);
|
||||
|
||||
// return tCVertexBufferCtorFn(This, a2, a3, a4, a5,
|
||||
// count,
|
||||
// bufSize,
|
||||
// source, a6, a7);
|
||||
}
|
||||
|
||||
void __declspec(naked) nakedCVertexBufferCtorFn() {
|
||||
__asm {
|
||||
call hCVertexBufferCtorFn
|
||||
jmp nCVertexBufferCtorFn
|
||||
};
|
||||
}
|
||||
|
||||
int patchDynamicVB(HMODULE hDll) {
|
||||
const char* pat = "0F B6 C8 8B 45 10 51 8B 4D FC 99 68 ?? ?? ?? ?? 68 00 80 00 00";
|
||||
size_t offset_imm = 0xe13b - 0xe12a;
|
||||
size_t offset_call = 0xe152 - 0xe12a;
|
||||
std::uint8_t* ptr = Memory::PatternScan(hDll, pat, 0);
|
||||
if (ptr == nullptr) {
|
||||
spdlog::info(L"Can not find the DynamicVB call");
|
||||
return -1;
|
||||
}
|
||||
spdlog::info(L"Hook DynamicVB Call success");
|
||||
|
||||
ptr += offset_call;
|
||||
spdlog::info(L"Modified the DynamicVB call @ 0x{:0x}", (intptr_t)ptr - (intptr_t)hDll);
|
||||
oCVertexBufferCtorFn = reinterpret_cast<CVertexBufferCtorFn>(ptr);
|
||||
nCVertexBufferCtorFn = reinterpret_cast<intptr_t>(ptr) + 0x5;
|
||||
|
||||
// copy the target address, call is followed by an relative address
|
||||
const char* pat_GameCVertexBufferCtor = "55 8B EC 81 EC 08 01 00 00 A1 ?? ?? ?? ?? 33 C5 89 45 FC 53 56 57 8B 45 08 8B 55 10";
|
||||
std::uint8_t* ptr_GameCVertexBufferCtor = Memory::PatternScan(hDll, pat_GameCVertexBufferCtor, 0);
|
||||
if (ptr_GameCVertexBufferCtor == nullptr) {
|
||||
spdlog::info(L"Can not find the GameCVertexBufferCtor");
|
||||
return -1;
|
||||
}
|
||||
tCVertexBufferCtorFn = reinterpret_cast<CVertexBufferCtorFn>(ptr_GameCVertexBufferCtor);
|
||||
spdlog::info(L"Hook GameCVertexBufferCtor success");
|
||||
|
||||
// Only for debugging
|
||||
// Memory::Write((uintptr_t)hDll + 0xbb67, (uint16_t)0x9090);
|
||||
|
||||
spdlog::info(L"New DynamicVB call to 0x{:0x}", (intptr_t)oCVertexBufferCtorFn);
|
||||
|
||||
DetourRestoreAfterWith();
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread(GetCurrentThread());
|
||||
DetourAttach(&(PVOID&)oCVertexBufferCtorFn, nakedCVertexBufferCtorFn);
|
||||
DetourTransactionCommit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
using GetMaxToRenderFn = int (__thiscall*)(void*, DWORD*, char, int*, int*);
|
||||
GetMaxToRenderFn oGetMaxToRender = nullptr;
|
||||
|
||||
int __fastcall hGetMaxToRender(
|
||||
void* This,
|
||||
DWORD EDX,
|
||||
DWORD* a2,
|
||||
char a3,
|
||||
int* p_bufsize,
|
||||
int* p_nCount
|
||||
) {
|
||||
auto ret = oGetMaxToRender(This, a2, a3, p_bufsize, p_nCount);
|
||||
|
||||
// if (cfg::GetMaxToRender::debug) {
|
||||
// if (p_bufsize != nullptr && p_nCount != nullptr) {
|
||||
// spdlog::debug("p_bufSize: {} {:x}, p_nCount: {} {:x}", *p_bufsize, *p_bufsize, *p_nCount, *p_nCount);
|
||||
// } else {
|
||||
// spdlog::debug("invalid pointer, omitting, {}, {}", (intptr_t)p_bufsize, (intptr_t)p_nCount);
|
||||
// }
|
||||
// }
|
||||
|
||||
if (p_bufsize != nullptr && *p_bufsize == cfg::GetMaxToRender::old_value) *p_bufsize = cfg::GetMaxToRender::new_value;
|
||||
return ret;
|
||||
}
|
||||
|
||||
int patchGetMaxToRender(HMODULE hDll) {
|
||||
const char* pat_GetMaxToRender = "55 8B EC 57 8B 7D 08 85 FF 75 ?? 8B 45 10 89 38";
|
||||
std::uint8_t* ptr_GetMaxToRender = Memory::PatternScan(hDll, pat_GetMaxToRender, 0);
|
||||
oGetMaxToRender = reinterpret_cast<GetMaxToRenderFn>(ptr_GetMaxToRender);
|
||||
if (oGetMaxToRender == nullptr) {
|
||||
spdlog::info(L"Can not find the GetMaxToRender");
|
||||
return -1;
|
||||
}
|
||||
spdlog::info(L"Hook GetMaxToRender success");
|
||||
|
||||
DetourRestoreAfterWith();
|
||||
DetourTransactionBegin();
|
||||
DetourUpdateThread(GetCurrentThread());
|
||||
DetourAttach(&(PVOID&)oGetMaxToRender, hGetMaxToRender);
|
||||
DetourTransactionCommit();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------------
|
||||
|
||||
int hooks_dvb(HMODULE hDll, std::wstring dllName) {
|
||||
auto ret = 0;
|
||||
|
||||
if (cfg::DynamicVB::enable) {
|
||||
ret += patchDynamicVB(hDll);
|
||||
}
|
||||
|
||||
if (cfg::GetMaxToRender::enable) {
|
||||
ret += patchGetMaxToRender(hDll);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
#pragma once
|
||||
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
#include <string>
|
||||
#include <ranges>
|
||||
|
||||
#include "helper.hpp"
|
||||
#include "vars.h"
|
||||
|
||||
int PatchIndices(HMODULE hDll, std::wstring dllName) {
|
||||
if (!cfg::Indices::enable) return 0;
|
||||
|
||||
// Tag, pattern, start_search_path, write_offset
|
||||
auto f = [&](const wchar_t* tag, const char* pattern, size_t offset, size_t w_offset) {
|
||||
std::uint8_t* ptr = Memory::PatternScan(hDll, pattern, offset);
|
||||
|
||||
auto new_buffer_size = cfg::Indices::value;
|
||||
|
||||
if (ptr != nullptr) {
|
||||
Memory::Write((uintptr_t)(ptr + w_offset), new_buffer_size);
|
||||
spdlog::info(L"Modified the vertex max buffer {}: {} @ 0x{:0x}", dllName, tag, (intptr_t)(ptr) - (intptr_t)hDll);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
spdlog::error(L"Pattern scan failed for {}: {}", dllName, tag);
|
||||
return (uint8_t*)nullptr;
|
||||
};
|
||||
|
||||
|
||||
std::vector<void*> ptr_cache;
|
||||
std::string pattern;
|
||||
|
||||
// CreateEmptyColorMesh
|
||||
pattern = "68 ?? ?? ?? ?? 68 00 80 00 00 6A 04";
|
||||
auto* ptr1 = f(
|
||||
L"CreateEmptyColorMesh",
|
||||
pattern.c_str(),
|
||||
0,
|
||||
6
|
||||
);
|
||||
ptr_cache.push_back(ptr1);
|
||||
|
||||
pattern = "68 00 80 00 00 50 8B";
|
||||
auto* ptr1_1 = f(
|
||||
L"CreateEmptyColorMesh Later",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr1,
|
||||
1
|
||||
);
|
||||
ptr_cache.push_back(ptr1_1);
|
||||
|
||||
// CreateVertexIDBuffer
|
||||
pattern = "68 ?? ?? ?? ?? 68 00 80 00 00 6A 04";
|
||||
auto* ptr2 = f(
|
||||
L"CreateVertexIDBuffer",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr1_1,
|
||||
6
|
||||
);
|
||||
ptr_cache.push_back(ptr2);
|
||||
|
||||
pattern = "68 00 80 00 00 50 8B";
|
||||
auto* ptr2_1 = f(
|
||||
L"CreateVertexIDBuffer Later",
|
||||
pattern.c_str(),
|
||||
(size_t)ptr2,
|
||||
1
|
||||
);
|
||||
ptr_cache.push_back(ptr2_1);
|
||||
|
||||
auto* ptr3 = f(
|
||||
L"Unknown",
|
||||
"0F B6 C0 50 68 00 80 00 00 53 8B CF E8 ?? ?? ?? ??",
|
||||
(size_t)ptr2,
|
||||
5
|
||||
);
|
||||
ptr_cache.push_back(ptr3);
|
||||
|
||||
if (std::ranges::any_of(ptr_cache, [](void* ptr) { return ptr == nullptr; })) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
BIN
Binary file not shown.
+88
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
#define SPDLOG_WCHAR_TO_UTF8_SUPPORT
|
||||
#include <spdlog/spdlog.h>
|
||||
#include <spdlog/sinks/basic_file_sink.h>
|
||||
|
||||
#include <windows.h>
|
||||
#include <string>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
|
||||
#include "inipp.h"
|
||||
|
||||
HMODULE baseModule = GetModuleHandle(NULL);
|
||||
HMODULE thisModule; // Fix DLL
|
||||
|
||||
// Version
|
||||
std::string sFixName = "L4D2Fix";
|
||||
std::string sFixVer = "1.3.0";
|
||||
std::string sLogFile = sFixName + ".log";
|
||||
|
||||
// Logger
|
||||
std::shared_ptr<spdlog::logger> logger;
|
||||
std::filesystem::path sExePath;
|
||||
std::string sExeName;
|
||||
std::filesystem::path sThisModulePath;
|
||||
|
||||
// Ini Config
|
||||
inipp::Ini<char> ini;
|
||||
|
||||
// Global config vars
|
||||
namespace cfg {
|
||||
namespace System {
|
||||
bool debug;
|
||||
}
|
||||
|
||||
namespace Indices {
|
||||
bool enable;
|
||||
bool debug;
|
||||
int value;
|
||||
}
|
||||
|
||||
namespace DynamicVB {
|
||||
bool enable;
|
||||
bool debug;
|
||||
int value;
|
||||
}
|
||||
|
||||
namespace GetMaxToRender {
|
||||
bool enable;
|
||||
bool debug;
|
||||
int old_value;
|
||||
int new_value;
|
||||
}
|
||||
}
|
||||
|
||||
void LoadIni() {
|
||||
using namespace cfg;
|
||||
|
||||
std::ifstream is("kpatch.ini");
|
||||
ini.parse(is);
|
||||
|
||||
{
|
||||
using namespace System;
|
||||
inipp::extract(ini.sections["System"]["debug"], debug);
|
||||
}
|
||||
|
||||
{
|
||||
using namespace Indices;
|
||||
inipp::extract(ini.sections["Indices"]["enable"], enable);
|
||||
inipp::extract(ini.sections["Indices"]["debug"], debug);
|
||||
inipp::extract(ini.sections["Indices"]["value"], value);
|
||||
}
|
||||
|
||||
{
|
||||
using namespace DynamicVB;
|
||||
inipp::extract(ini.sections["DynamicVB"]["enable"], enable);
|
||||
inipp::extract(ini.sections["DynamicVB"]["debug"], debug);
|
||||
inipp::extract(ini.sections["DynamicVB"]["value"], value);
|
||||
}
|
||||
|
||||
{
|
||||
using namespace GetMaxToRender;
|
||||
inipp::extract(ini.sections["GetMaxToRender"]["enable"], enable);
|
||||
inipp::extract(ini.sections["GetMaxToRender"]["debug"], debug);
|
||||
inipp::extract(ini.sections["GetMaxToRender"]["old_value"], old_value);
|
||||
inipp::extract(ini.sections["GetMaxToRender"]["new_value"], new_value);
|
||||
}
|
||||
}
|
||||
@@ -10,16 +10,13 @@ set_arch("x86") -- Use "x64" for 64-bit builds
|
||||
add_requires("spdlog")
|
||||
add_requires("minhook")
|
||||
add_requires("vcpkg::detours")
|
||||
|
||||
if is_mode("release") then
|
||||
add_defines("NDEBUG", "NTESTING")
|
||||
end
|
||||
add_requires("vcpkg::inipp")
|
||||
|
||||
local name = "kpatch"
|
||||
target(name)
|
||||
set_kind("shared")
|
||||
add_files("src/dllmain.cpp")
|
||||
add_packages("spdlog", "vcpkg::detours", "minhook")
|
||||
add_packages("spdlog", "vcpkg::detours", "minhook", "vcpkg::inipp")
|
||||
add_links("user32", "gdi32")
|
||||
|
||||
local name = "left4dead2_fix"
|
||||
@@ -29,17 +26,3 @@ target(name)
|
||||
add_files("assets/app.rc")
|
||||
add_links("user32", "gdi32")
|
||||
add_packages("vcpkg::detours")
|
||||
|
||||
|
||||
rule("mode.release.testing")
|
||||
on_config(function (target)
|
||||
if is_mode("release.testing") then
|
||||
target:add_defines("NDEBUG")
|
||||
end
|
||||
end)
|
||||
|
||||
rule("mode.testing")
|
||||
on_config(function (target)
|
||||
if is_mode("testing") then
|
||||
end
|
||||
end)
|
||||
|
||||
Reference in New Issue
Block a user