Skip to content

Commit f82d968

Browse files
committed
modified: includes/helper.hpp
modified: source/fixes/PrisonerOfWarWidescreenFix/dllmain.cpp
1 parent 3b3abec commit f82d968

2 files changed

Lines changed: 153 additions & 144 deletions

File tree

includes/helper.hpp

Lines changed: 100 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -165,52 +165,121 @@ namespace Memory
165165
VirtualProtect(address, numBytes, oldProtect, &oldProtect);
166166
}
167167

168-
inline bool PatchCallRel32(uint8_t* callSite, uint8_t* target, std::array<uint8_t, 5>* out_orig)
169-
{
170-
if (!callSite || !target)
171-
{
172-
return false;
173-
}
168+
enum class CallType
169+
{
170+
Relative,
171+
Absolute
172+
};
174173

175-
if (callSite[0] != 0xE8)
176-
{
177-
return false;
178-
}
174+
inline std::optional<std::vector<uint8_t>>
175+
PatchCALL(uint8_t* callSite, uint8_t* target, CallType type = CallType::Relative)
176+
{
177+
if (!callSite || !target)
178+
{
179+
spdlog::error("PatchCALL failed: null pointer (callSite={}, target={})", fmt::ptr(callSite), fmt::ptr(target));
179180

180-
if (out_orig) std::memcpy(out_orig->data(), callSite, 5);
181+
return std::nullopt;
182+
}
181183

182-
intptr_t nextInstr = reinterpret_cast<intptr_t>(callSite) + 5;
184+
DWORD oldProt{};
183185

184-
intptr_t rel64 = reinterpret_cast<intptr_t>(target) - nextInstr;
186+
if (type == CallType::Relative)
187+
{
188+
intptr_t nextInstr = reinterpret_cast<intptr_t>(callSite) + 5;
189+
intptr_t rel64 = reinterpret_cast<intptr_t>(target) - nextInstr;
185190

186-
if (rel64 < INT32_MIN || rel64 > INT32_MAX)
187-
{
188-
return false;
189-
}
191+
if (rel64 < INT32_MIN || rel64 > INT32_MAX)
192+
{
193+
spdlog::error("PatchCALL (Relative) failed: rel32 overflow.");
194+
return std::nullopt;
195+
}
190196

191-
int32_t rel32 = static_cast<int32_t>(rel64);
197+
std::vector<uint8_t> original(5);
198+
std::memcpy(original.data(), callSite, 5);
192199

193-
uint8_t patch[5];
200+
int32_t rel32 = static_cast<int32_t>(rel64);
194201

195-
patch[0] = 0xE8;
202+
uint8_t patch[5];
203+
patch[0] = 0xE8;
204+
std::memcpy(&patch[1], &rel32, sizeof(rel32));
196205

197-
std::memcpy(&patch[1], &rel32, sizeof(rel32));
206+
if (!VirtualProtect(callSite, 5, PAGE_EXECUTE_READWRITE, &oldProt))
207+
{
208+
spdlog::error("PatchCALL failed: VirtualProtect.");
209+
return std::nullopt;
210+
}
198211

199-
DWORD oldProt;
212+
std::memcpy(callSite, patch, 5);
200213

201-
if (!VirtualProtect(callSite, 5, PAGE_EXECUTE_READWRITE, &oldProt))
202-
{
203-
return false;
204-
}
214+
FlushInstructionCache(GetCurrentProcess(), callSite, 5);
205215

206-
std::memcpy(callSite, patch, 5);
216+
VirtualProtect(callSite, 5, oldProt, &oldProt);
207217

208-
FlushInstructionCache(GetCurrentProcess(), callSite, 5);
218+
spdlog::info("PatchCALL (Relative): {:#x} -> {:#x}", reinterpret_cast<uintptr_t>(callSite), reinterpret_cast<uintptr_t>(target));
209219

210-
VirtualProtect(callSite, 5, oldProt, &oldProt);
220+
return original;
221+
}
222+
else
223+
{
224+
#if defined(_WIN64)
225+
constexpr size_t patchSize = 12;
211226

212-
return true;
213-
}
227+
std::vector<uint8_t> original(patchSize);
228+
std::memcpy(original.data(), callSite, patchSize);
229+
230+
uint8_t patch[patchSize] = { 0 };
231+
232+
patch[0] = 0x48;
233+
patch[1] = 0xB8; // mov rax, imm64
234+
std::memcpy(&patch[2], &target, sizeof(uint64_t));
235+
236+
patch[10] = 0xFF;
237+
patch[11] = 0xD0; // call rax
238+
239+
if (!VirtualProtect(callSite, patchSize, PAGE_EXECUTE_READWRITE, &oldProt))
240+
{
241+
spdlog::error("PatchCALL (Absolute) failed: VirtualProtect.");
242+
return std::nullopt;
243+
}
244+
245+
std::memcpy(callSite, patch, patchSize);
246+
FlushInstructionCache(GetCurrentProcess(), callSite, patchSize);
247+
VirtualProtect(callSite, patchSize, oldProt, &oldProt);
248+
249+
spdlog::info("PatchCALL (Absolute x64): {:#x} -> {:#x}",
250+
reinterpret_cast<uintptr_t>(callSite),
251+
reinterpret_cast<uintptr_t>(target));
252+
253+
return original;
254+
255+
#else
256+
constexpr size_t patchSize = 6;
257+
258+
std::vector<uint8_t> original(patchSize);
259+
std::memcpy(original.data(), callSite, patchSize);
260+
261+
uint8_t patch[patchSize];
262+
263+
patch[0] = 0x68;
264+
std::memcpy(&patch[1], &target, sizeof(uint32_t));
265+
patch[5] = 0xC3;
266+
267+
if (!VirtualProtect(callSite, patchSize, PAGE_EXECUTE_READWRITE, &oldProt))
268+
{
269+
spdlog::error("PatchCALL (Absolute) failed: VirtualProtect.");
270+
return std::nullopt;
271+
}
272+
273+
std::memcpy(callSite, patch, patchSize);
274+
FlushInstructionCache(GetCurrentProcess(), callSite, patchSize);
275+
VirtualProtect(callSite, patchSize, oldProt, &oldProt);
276+
277+
spdlog::info("PatchCALL (Absolute x86): {:#x} -> {:#x}", reinterpret_cast<uintptr_t>(callSite), reinterpret_cast<uintptr_t>(target));
278+
279+
return original;
280+
#endif
281+
}
282+
}
214283

215284
static inline int hexval(char c) noexcept
216285
{

source/fixes/PrisonerOfWarWidescreenFix/dllmain.cpp

Lines changed: 53 additions & 113 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ HMODULE thisModule;
2727

2828
// Fix details
2929
std::string sFixName = "PrisonerOfWarWidescreenFix";
30-
std::string sFixVersion = "1.2";
30+
std::string sFixVersion = "1.3";
3131
std::filesystem::path sFixPath;
3232

3333
// Ini
@@ -43,6 +43,7 @@ std::string sExeName;
4343
// Constants
4444
constexpr float fOldAspectRatio = 4.0f / 3.0f;
4545
constexpr float fNewAspectRatio2 = 0.75f;
46+
constexpr float fNewFrustumCullingFOV = 3.1f;
4647

4748
// Ini variables
4849
bool bFixActive;
@@ -53,8 +54,7 @@ float fFOVFactor;
5354
// Variables
5455
float fNewAspectRatio;
5556
float fAspectRatioScale;
56-
float fNewCameraFOV1;
57-
float fNewCameraFOV2;
57+
float fNewGameplayFOV;
5858
float fNewCameraHFOV;
5959

6060
// Game detection
@@ -67,6 +67,20 @@ enum class Game
6767
Unknown
6868
};
6969

70+
enum AspectRatioInstructionsIndices
71+
{
72+
AR1,
73+
Codecave,
74+
AR2,
75+
HFOV
76+
};
77+
78+
enum CameraFOVInstructionsIndices
79+
{
80+
GameplayFOV,
81+
FrustumCullingFOV
82+
};
83+
7084
struct GameInfo
7185
{
7286
std::string GameTitle;
@@ -197,8 +211,8 @@ bool DetectGame()
197211
return false;
198212
}
199213

200-
static SafetyHookMid CameraFOVInstruction1Hook{};
201-
static SafetyHookMid CameraFOVInstruction2Hook{};
214+
static SafetyHookMid GameplayCameraFOVInstructionHook{};
215+
static SafetyHookMid FrustumCullingFOVInstructionHook{};
202216
static SafetyHookMid AspectRatioInstruction1Hook{};
203217
static SafetyHookMid CameraHFOVInstructionHook{};
204218

@@ -238,141 +252,67 @@ void FOVFix()
238252

239253
fAspectRatioScale = fNewAspectRatio / fOldAspectRatio;
240254

241-
std::uint8_t* AspectRatioInstruction1ScanResult = Memory::PatternScan(exeModule, "DB 05 ?? ?? ?? ?? 8B C1");
242-
if (AspectRatioInstruction1ScanResult)
255+
std::vector<std::uint8_t*> AspectRatioInstructionsScansResult = Memory::PatternScan(exeModule, "DB 05 ?? ?? ?? ?? 8B C1", "7A 2E 65 78 65", "E8 ?? ?? ?? ?? D9 05 ?? ?? ?? ?? D8 4E", "D8 3D ?? ?? ?? ?? D9 18");
256+
if (Memory::AreAllSignaturesValid(AspectRatioInstructionsScansResult) == true)
243257
{
244-
spdlog::info("Aspect Ratio Instruction 1: Address is {:s}+{:x}", sExeName.c_str(), AspectRatioInstruction1ScanResult - (std::uint8_t*)exeModule);
258+
spdlog::info("Aspect Ratio Instruction 1: Address is {:s}+{:x}", sExeName.c_str(), AspectRatioInstructionsScansResult[AR1] - (std::uint8_t*)exeModule);
245259

246-
Memory::PatchBytes(AspectRatioInstruction1ScanResult, "\x90\x90\x90\x90\x90\x90", 6);
260+
spdlog::info("Camera HFOV Instruction: Address is {:s}+{:x}", sExeName.c_str(), AspectRatioInstructionsScansResult[HFOV] - (std::uint8_t*)exeModule);
247261

248-
AspectRatioInstruction1Hook = safetyhook::create_mid(AspectRatioInstruction1ScanResult, [](SafetyHookContext& ctx)
262+
Memory::WriteNOPs(AspectRatioInstructionsScansResult[AR1], 6);
263+
264+
AspectRatioInstruction1Hook = safetyhook::create_mid(AspectRatioInstructionsScansResult[AR1], [](SafetyHookContext& ctx)
249265
{
250266
FPU::FLD(fNewAspectRatio2);
251267
});
252268

253-
Memory::PatchBytes(AspectRatioInstruction1ScanResult + 10, "\x90\x90\x90\x90\x90\x90", 6);
254-
}
255-
else
256-
{
257-
spdlog::error("Failed to locate aspect ratio instruction 1 memory address.");
258-
return;
259-
}
260-
261-
std::uint8_t* CameraFOVInstruction1ScanResult = Memory::PatternScan(exeModule, "8B 90 E0 00 00 00 89 88 DC 00 00 00 52 8B 54 24 08 8D 48 4C 8B 80 D8 00 00 00 52 50");
262-
if (CameraFOVInstruction1ScanResult)
263-
{
264-
spdlog::info("Camera FOV Instruction 1: Address is {:s}+{:x}", sExeName.c_str(), CameraFOVInstruction1ScanResult - (std::uint8_t*)exeModule);
265-
266-
Memory::PatchBytes(CameraFOVInstruction1ScanResult, "\x90\x90\x90\x90\x90\x90", 6);
269+
Memory::WriteNOPs(AspectRatioInstructionsScansResult[AR1] + 10, 6);
267270

268-
CameraFOVInstruction1Hook = safetyhook::create_mid(CameraFOVInstruction1ScanResult, [](SafetyHookContext& ctx)
271+
if (eGameType == Game::POWGAME_EN)
269272
{
270-
float& fCurrentCameraFOV1 = *reinterpret_cast<float*>(ctx.eax + 0xE0);
271-
272-
if (fCurrentCameraFOV1 == 0.959931492805481f)
273-
{
274-
fNewCameraFOV1 = fCurrentCameraFOV1 * fFOVFactor;
275-
}
276-
else
277-
{
278-
fNewCameraFOV1 = fCurrentCameraFOV1;
279-
}
280-
281-
ctx.edx = std::bit_cast<uintptr_t>(fNewCameraFOV1);
282-
});
283-
}
284-
else
285-
{
286-
spdlog::error("Failed to locate camera FOV instruction 1 memory address.");
287-
return;
288-
}
289-
290-
std::uint8_t* CameraFOVInstruction2ScanResult = Memory::PatternScan(exeModule, "90 90 90 90 90 90 D9 81 E0 00 00 00");
291-
if (CameraFOVInstruction2ScanResult)
292-
{
293-
spdlog::info("Camera FOV Instruction 2: Address is {:s}+{:x}", sExeName.c_str(), CameraFOVInstruction2ScanResult + 6 - (std::uint8_t*)exeModule);
294-
295-
Memory::PatchBytes(CameraFOVInstruction2ScanResult + 6, "\x90\x90\x90\x90\x90\x90", 6);
296-
297-
CameraFOVInstruction2Hook = safetyhook::create_mid(CameraFOVInstruction2ScanResult + 6, [](SafetyHookContext& ctx)
273+
Memory::PatchBytes(AspectRatioInstructionsScansResult[Codecave] + 6, "\xDB\x05\xC8\x70\x66\x00\x8B\xC1\x33\xC9\xDA\x35\xC4\x70\x66\x00\xC7\x40\x2C\x00\x00\x80\x3F\x89\x48\x3C\x89\x48\x34\x89\x48\x30\x89\x48\x24\x89\x48\x20\x89\x48\x1C\x89\x48\x18\x89\x48\x10\x89\x48\x0C\x89\x48\x08\x89\x48\x04\xD8\x4C\x24\x0C\xD8\x0D\xCC\xC2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xC4\x60\x00\xD9\x44\x24\x08\xD8\x64\x24\x04\xD8\x7C\x24\x08\xD9\x44\x24\x0C\xD8\x0D\xCC\xC2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xC4\x60\x00\xD9\x18\xD9\xC9\xD9\x58\x14\xD9\x50\x28\xD8\x4C\x24\x04\xD9\xE0\xD9\x58\x38\xC2\x0C\x00");
274+
}
275+
else if (eGameType == Game::POWGAME_RU)
298276
{
299-
float& fCurrentCameraFOV2 = *reinterpret_cast<float*>(ctx.ecx + 0xE0);
300-
301-
if (fCurrentCameraFOV2 == 0.959931492805481f)
302-
{
303-
fNewCameraFOV2 = Maths::CalculateNewFOV_RadBased(fCurrentCameraFOV2, fAspectRatioScale) * fFOVFactor;
304-
}
305-
else
306-
{
307-
fNewCameraFOV2 = Maths::CalculateNewFOV_RadBased(fCurrentCameraFOV2, fAspectRatioScale);
308-
}
309-
310-
FPU::FLD(fNewCameraFOV2);
311-
});
312-
}
313-
else
314-
{
315-
spdlog::error("Failed to locate camera FOV instruction 2 memory address.");
316-
return;
317-
}
277+
Memory::PatchBytes(AspectRatioInstructionsScansResult[Codecave] + 6, "\xDB\x05\x00\xDA\x66\x00\x8B\xC1\x33\xC9\xDA\x35\xFC\xD9\x66\x00\xC7\x40\x2C\x00\x00\x80\x3F\x89\x48\x3C\x89\x48\x34\x89\x48\x30\x89\x48\x24\x89\x48\x20\x89\x48\x1C\x89\x48\x18\x89\x48\x10\x89\x48\x0C\x89\x48\x08\x89\x48\x04\xD8\x4C\x24\x0C\xD8\x0D\xCC\xD2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xD4\x60\x00\xD9\x44\x24\x08\xD8\x64\x24\x04\xD8\x7C\x24\x08\xD9\x44\x24\x0C\xD8\x0D\xCC\xD2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xD4\x60\x00\xD9\x18\xD9\xC9\xD9\x58\x14\xD9\x50\x28\xD8\x4C\x24\x04\xD9\xE0\xD9\x58\x38\xC2\x0C\x00");
278+
}
318279

319-
std::uint8_t* CameraHFOVInstructionScanResult = Memory::PatternScan(exeModule, "D8 3D ?? ?? ?? ?? D9 18 D9 C9 D9 58 14 D9 50 28 D8 4C 24 04 D9 E0 D9 58 38 C2 0C 00");
320-
if (CameraHFOVInstructionScanResult)
321-
{
322-
spdlog::info("Camera HFOV Instruction: Address is {:s}+{:x}", sExeName.c_str(), CameraHFOVInstructionScanResult - (std::uint8_t*)exeModule);
280+
Memory::PatchCALL(AspectRatioInstructionsScansResult[AR2], AspectRatioInstructionsScansResult[Codecave] + 6, Memory::CallType::Relative);
323281

324-
Memory::PatchBytes(CameraHFOVInstructionScanResult, "\x90\x90\x90\x90\x90\x90", 6);
282+
Memory::WriteNOPs(AspectRatioInstructionsScansResult[HFOV], 6);
325283

326284
fNewCameraHFOV = 1.0f / fAspectRatioScale;
327285

328-
CameraHFOVInstructionHook = safetyhook::create_mid(CameraHFOVInstructionScanResult, [](SafetyHookContext& ctx)
286+
CameraHFOVInstructionHook = safetyhook::create_mid(AspectRatioInstructionsScansResult[HFOV], [](SafetyHookContext& ctx)
329287
{
330288
FPU::FDIVR(fNewCameraHFOV);
331289
});
332290
}
333-
else
334-
{
335-
spdlog::error("Failed to locate camera HFOV instruction memory address.");
336-
return;
337-
}
338291

339-
std::uint8_t* CodecaveScanResult = Memory::PatternScan(exeModule, "7A 2E 65 78 65");
340-
if (CodecaveScanResult)
292+
std::vector<std::uint8_t*> CameraFOVInstructionsScansResult = Memory::PatternScan(exeModule, "D9 06 D8 0D ?? ?? ?? ?? 51", "D9 81 ?? ?? ?? ?? C3 90 90 90 90 90 90 90 90 90 8B 44 24 ?? 8B 54 24");
293+
if (Memory::AreAllSignaturesValid(CameraFOVInstructionsScansResult) == true)
341294
{
342-
spdlog::info("Codecave Scan: Address is {:s}+{:x}", sExeName.c_str(), CodecaveScanResult - (std::uint8_t*)exeModule);
295+
spdlog::info("Gameplay Camera FOV Instruction: Address is {:s}+{:x}", sExeName.c_str(), CameraFOVInstructionsScansResult[GameplayFOV] - (std::uint8_t*)exeModule);
343296

344-
if (eGameType == Game::POWGAME_EN)
345-
{
346-
Memory::PatchBytes(CodecaveScanResult + 6, "\xDB\x05\xC8\x70\x66\x00\x8B\xC1\x33\xC9\xDA\x35\xC4\x70\x66\x00\xC7\x40\x2C\x00\x00\x80\x3F\x89\x48\x3C\x89\x48\x34\x89\x48\x30\x89\x48\x24\x89\x48\x20\x89\x48\x1C\x89\x48\x18\x89\x48\x10\x89\x48\x0C\x89\x48\x08\x89\x48\x04\xD8\x4C\x24\x0C\xD8\x0D\xCC\xC2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xC4\x60\x00\xD9\x44\x24\x08\xD8\x64\x24\x04\xD8\x7C\x24\x08\xD9\x44\x24\x0C\xD8\x0D\xCC\xC2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xC4\x60\x00\xD9\x18\xD9\xC9\xD9\x58\x14\xD9\x50\x28\xD8\x4C\x24\x04\xD9\xE0\xD9\x58\x38\xC2\x0C\x00", 130);
347-
}
348-
else if (eGameType == Game::POWGAME_RU)
349-
{
350-
Memory::PatchBytes(CodecaveScanResult + 6, "\xDB\x05\x00\xDA\x66\x00\x8B\xC1\x33\xC9\xDA\x35\xFC\xD9\x66\x00\xC7\x40\x2C\x00\x00\x80\x3F\x89\x48\x3C\x89\x48\x34\x89\x48\x30\x89\x48\x24\x89\x48\x20\x89\x48\x1C\x89\x48\x18\x89\x48\x10\x89\x48\x0C\x89\x48\x08\x89\x48\x04\xD8\x4C\x24\x0C\xD8\x0D\xCC\xD2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xD4\x60\x00\xD9\x44\x24\x08\xD8\x64\x24\x04\xD8\x7C\x24\x08\xD9\x44\x24\x0C\xD8\x0D\xCC\xD2\x60\x00\xD9\xF2\xDD\xD8\xD8\x3D\x44\xD4\x60\x00\xD9\x18\xD9\xC9\xD9\x58\x14\xD9\x50\x28\xD8\x4C\x24\x04\xD9\xE0\xD9\x58\x38\xC2\x0C\x00", 130);
351-
}
352-
}
353-
else
354-
{
355-
spdlog::error("Failed to locate codecave scan memory address.");
356-
return;
357-
}
297+
spdlog::info("Frustum Culling Camera FOV Instruction: Address is {:s}+{:x}", sExeName.c_str(), CameraFOVInstructionsScansResult[FrustumCullingFOV] - (std::uint8_t*)exeModule);
358298

359-
std::uint8_t* AspectRatioInstruction2ScanResult = Memory::PatternScan(exeModule, "E8 ?? ?? ?? ?? D9 05 ?? ?? ?? ?? D8 4E");
360-
if (AspectRatioInstruction2ScanResult)
361-
{
362-
spdlog::info("Aspect Ratio Instruction 2: Address is {:s}+{:x}", sExeName.c_str(), AspectRatioInstruction2ScanResult - (std::uint8_t*)exeModule);
299+
Memory::WriteNOPs(CameraFOVInstructionsScansResult[GameplayFOV], 2);
363300

364-
uint8_t* codecaveTarget = CodecaveScanResult + 6;
301+
GameplayCameraFOVInstructionHook = safetyhook::create_mid(CameraFOVInstructionsScansResult[GameplayFOV], [](SafetyHookContext& ctx)
302+
{
303+
float& fCurrentGameplayFOV = *reinterpret_cast<float*>(ctx.esi);
365304

366-
std::array<uint8_t, 5> saved{};
305+
fNewGameplayFOV = fCurrentGameplayFOV * fFOVFactor;
306+
307+
FPU::FLD(fNewGameplayFOV);
308+
});
367309

368-
if (Memory::PatchCallRel32(AspectRatioInstruction2ScanResult, codecaveTarget, &saved) == false)
369-
{
370-
spdlog::error("PatchCallRel32 failed (rel32 overflow or not a call).");
371-
}
372-
else
310+
Memory::WriteNOPs(CameraFOVInstructionsScansResult[FrustumCullingFOV], 6);
311+
312+
FrustumCullingFOVInstructionHook = safetyhook::create_mid(CameraFOVInstructionsScansResult[FrustumCullingFOV], [](SafetyHookContext& ctx)
373313
{
374-
spdlog::info("Successfully patched the CALL instruction located at {:s}+{:x} to the patched codecave located at {:s}+{:x}", sExeName.c_str(), AspectRatioInstruction2ScanResult - (uint8_t*)exeModule, sExeName.c_str(), (uintptr_t)codecaveTarget);
375-
}
314+
FPU::FLD(fNewFrustumCullingFOV);
315+
});
376316
}
377317
}
378318
}

0 commit comments

Comments
 (0)