mirror of
https://github.com/isledecomp/LEGOIslandRebuilder.git
synced 2024-11-30 10:57:49 -05:00
232 lines
9.3 KiB
C++
232 lines
9.3 KiB
C++
#include <IOSTREAM>
|
|
#include <TCHAR.H>
|
|
#include <VECTOR>
|
|
#include <WINDOWS.H>
|
|
|
|
#include "config.h"
|
|
#include "hooks.h"
|
|
#include "util.h"
|
|
|
|
DWORD WINAPI Patch()
|
|
{
|
|
if (!config.Load()) {
|
|
MessageBoxA(0, "Failed to find Rebuilder configuration. No patches will be active.", 0, 0);
|
|
return 1;
|
|
}
|
|
|
|
// Hook import address table
|
|
LPVOID exeBase = GetModuleHandle(TEXT("ISLE.EXE"));
|
|
LPVOID dllBase = GetModuleHandle(TEXT("LEGO1.DLL"));
|
|
|
|
// Redirect various imports
|
|
OverwriteImport(exeBase, "CreateWindowExA", (LPVOID)InterceptCreateWindowExA);
|
|
OverwriteImport(dllBase, "OutputDebugStringA", (LPVOID)InterceptOutputDebugStringA);
|
|
OverwriteImport(exeBase, "RegQueryValueExA", (LPVOID)InterceptRegQueryValueExA);
|
|
OverwriteImport(exeBase, "RegisterClassA", (LPVOID)InterceptRegisterClassA);
|
|
OverwriteImport(dllBase, "Sleep", (LPVOID)InterceptSleep);
|
|
OverwriteImport(exeBase, "Sleep", (LPVOID)InterceptSleep);
|
|
OverwriteImport(dllBase, "GetAsyncKeyState", (LPVOID)InterceptGetAsyncKeyState);
|
|
ddCreateOriginal = (ddCreateFunction)OverwriteImport(dllBase, "DirectDrawCreate", (LPVOID)InterceptDirectDrawCreate);
|
|
d3drmCreateOriginal = (d3drmCreateFunction)OverwriteImport(dllBase, "Direct3DRMCreate", (LPVOID)InterceptDirect3DRMCreate);
|
|
dsCreateOriginal = (dsCreateFunction)OverwriteImport(dllBase, "DirectSoundCreate", (LPVOID)InterceptDirectSoundCreate);
|
|
dinputCreateOriginal = (dinputCreateFunction)OverwriteImport(dllBase, "DirectInputCreateA", (LPVOID)InterceptDirectInputCreateA);
|
|
|
|
// Flip surfaces is incompatible with full screen, if these options are set, warn the user
|
|
if (config.GetInt(_T("FlipSurfaces")) && !config.GetInt(_T("FullScreen"))) {
|
|
if (MessageBoxA(0, "The setting 'Flip Video Memory Pages' is incompatible with LEGO Island's windowed mode. "
|
|
"LEGO Island will likely fail to start up unless you disable 'Flip Video Memory Pages' "
|
|
"or run in full screen mode. Do you wish to continue?", "Warning", MB_YESNO) == IDNO) {
|
|
TerminateProcess(GetCurrentProcess(), 0);
|
|
}
|
|
}
|
|
|
|
// Stay active when defocused
|
|
if (config.GetInt(_T("StayActiveWhenDefocused"))) {
|
|
// Patch jump if window isn't active (TODO: Replace with C++ patch)
|
|
SearchReplacePattern(exeBase, "\x89\x58\x70", "\x90\x90\x90", 3);
|
|
}
|
|
|
|
// Allow multiple instances
|
|
if (config.GetInt(_T("MultipleInstances"))) {
|
|
// Patch FindWindowA import to always tell ISLE that no other ISLE window exists
|
|
OverwriteImport(exeBase, "FindWindowA", (LPVOID)InterceptFindWindowA);
|
|
}
|
|
|
|
// Speed up startup
|
|
if (config.GetInt(_T("SpeedUpStartUp"))) {
|
|
// Replace "200" frame wait value with "1"
|
|
const char *speedup_pattern = "\xC8\x00\x00\x00\x00\x00\x00\x00";
|
|
const char *speedup_replace = "\x01\x00\x00\x00\x00\x00\x00\x00";
|
|
|
|
SearchReplacePattern(exeBase, speedup_pattern, speedup_replace, 8, TRUE);
|
|
}
|
|
|
|
// Debug mode
|
|
if (config.GetInt(_T("DebugToggle"))) {
|
|
// LEGO1 uses a string pointer to know if OGEL has been typed, if the string pointer sees 0x0
|
|
// (null-terminator/end of string), then debug mode is enabled. So we just replace the first
|
|
// character with 0x0 and it's permanently on.
|
|
SearchReplacePattern(dllBase, "OGEL", "\x0GEL", 4, TRUE);
|
|
}
|
|
|
|
// Disable auto-finish in build sections
|
|
if (config.GetInt(_T("DisableAutoFinishBuilding"))) {
|
|
// Pattern used in August build (jump is much shorter so it uses a different opcode)
|
|
const char *autofinish_pattern = "\x66\x39\x90\xBE\x00\x00\x00\x75";
|
|
const char *autofinish_replace = "\x66\x39\x90\xBE\x00\x00\x00\xEB";
|
|
|
|
if (SearchReplacePattern(dllBase, autofinish_pattern, autofinish_replace, 8) == 0) {
|
|
// Pattern used in September build (jump is much longer)
|
|
autofinish_pattern = "\x66\x39\x90\xBE\x00\x00\x00\x0F\x85\x86\x00\x00\x00";
|
|
autofinish_replace = "\x66\x39\x90\xBE\x00\x00\x00\xE9\x87\x00\x00\x00\x90";
|
|
SearchReplacePattern(dllBase, autofinish_pattern, autofinish_replace, 13);
|
|
}
|
|
}
|
|
|
|
// Unhook turn speed
|
|
float turn_max_spd = config.GetFloat(_T("TurnMaxSpeed"), 20.0f);
|
|
|
|
if (config.GetInt(_T("UnhookTurnSpeed"))) {
|
|
LPVOID code_offset = SearchPattern(dllBase, "\x74\x26\xD9\x46\x34", 5);
|
|
if (code_offset) {
|
|
const char *new_code = "\xD9\x46\x24\xD8\x4C\x24\x14\xD8\x4E\x34\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90";
|
|
WriteMemory(code_offset, (LPVOID)new_code, 36);
|
|
turn_max_spd *= 2.0f;
|
|
} else {
|
|
printf("Failed to find code for unhooking turn speed\n");
|
|
}
|
|
}
|
|
|
|
// Patch navigation
|
|
{
|
|
const int nav_block_sz = 0x30;
|
|
const char *nav_block_src = "\x28\x00\x00\x00\x6F\x12\x83\x3A\x00\x00\x20\x42\x00\x00\xA0\x41\x00\x00\x70\x41\x00\x00\xF0\x41\x00\x00\x80\x40\x00\x00\x70\x41\x00\x00\x48\x42\x00\x00\x48\x42\xCD\xCC\xCC\x3E\x00\x00\x00\x00";
|
|
char nav_block_dst[nav_block_sz];
|
|
memcpy(nav_block_dst, nav_block_src, nav_block_sz);
|
|
|
|
UINT32 mouse_deadzone = config.GetInt(_T("MouseDeadzone"), 40);
|
|
memcpy(nav_block_dst+0x0, &mouse_deadzone, sizeof(mouse_deadzone));
|
|
|
|
float movement_max_spd = config.GetFloat(_T("MovementMaxSpeed"), 40.0f);
|
|
memcpy(nav_block_dst+0x8, &movement_max_spd, sizeof(movement_max_spd));
|
|
|
|
// Value retrieved above
|
|
memcpy(nav_block_dst+0xC, &turn_max_spd, sizeof(turn_max_spd));
|
|
|
|
float movement_max_accel = config.GetFloat(_T("MovementMaxAcceleration"), 15.0f);
|
|
memcpy(nav_block_dst+0x10, &movement_max_accel, sizeof(movement_max_accel));
|
|
|
|
float turn_max_accel = config.GetFloat(_T("TurnMaxAcceleration"), 30.0f);
|
|
memcpy(nav_block_dst+0x14, &turn_max_accel, sizeof(turn_max_accel));
|
|
|
|
float movement_min_accel = config.GetFloat(_T("MovementMinAcceleration"), 4.0f);
|
|
memcpy(nav_block_dst+0x18, &movement_min_accel, sizeof(movement_min_accel));
|
|
|
|
float turn_min_accel = config.GetFloat(_T("TurnMinAcceleration"), 15.0f);
|
|
memcpy(nav_block_dst+0x1C, &turn_min_accel, sizeof(turn_min_accel));
|
|
|
|
float movement_decel = config.GetFloat(_T("MovementDeceleration"), 50.0f);
|
|
memcpy(nav_block_dst+0x20, &movement_decel, sizeof(movement_decel));
|
|
|
|
float turn_decel = config.GetFloat(_T("TurnDeceleration"), 50.0f);
|
|
memcpy(nav_block_dst+0x24, &turn_decel, sizeof(turn_decel));
|
|
|
|
UINT32 turn_use_velocity = config.GetInt(_T("TurnUseVelocity"), FALSE);
|
|
memcpy(nav_block_dst+0x2C, &turn_use_velocity, sizeof(turn_use_velocity));
|
|
|
|
SearchReplacePattern(dllBase, nav_block_src, nav_block_dst, nav_block_sz);
|
|
}
|
|
|
|
// Model Quality
|
|
std::string model_quality = config.GetString("ModelQuality");
|
|
float mq_val = 3.6f;
|
|
if (model_quality == "Infinite") {
|
|
mq_val = 999999.0f;
|
|
} else if (model_quality == "High") {
|
|
mq_val = 5.0f;
|
|
} else if (model_quality == "Medium") {
|
|
mq_val = 3.6f;
|
|
} else if (model_quality == "Low") {
|
|
mq_val = 0.0f;
|
|
}
|
|
const char *mq_pattern = "\x00\x00\x80\x40\x66\x66\x66\x40";
|
|
char mq_replace[8];
|
|
memcpy(mq_replace, mq_pattern, 8);
|
|
memcpy(mq_replace+4, &mq_val, sizeof(mq_val));
|
|
SearchReplacePattern(dllBase, mq_pattern, mq_replace, 8);
|
|
|
|
// Transition Animation
|
|
std::string animation_type = config.GetString("TransitionType");
|
|
int anim_val = 3;
|
|
if (animation_type == "No Animation") {
|
|
anim_val = 1;
|
|
} else if (animation_type == "Dissolve") {
|
|
anim_val = 2;
|
|
} else if (animation_type == "Pixelation") {
|
|
anim_val = 3;
|
|
} else if (animation_type == "Vertical Wipe") {
|
|
anim_val = 4;
|
|
} else if (animation_type == "Window") {
|
|
anim_val = 5;
|
|
}
|
|
const char *at_pattern = "\x89\x46\x2C\x8A\x44\x24\x14\x32\xC1\x24\x01\x32\xC1";
|
|
char at_replace[13];
|
|
memcpy(at_replace, at_pattern, 13);
|
|
memcpy(at_replace, "\xC7", 1);
|
|
memcpy(at_replace+3, &anim_val, sizeof(anim_val));
|
|
memcpy(at_replace+11, "\xB0\x00", 2);
|
|
SearchReplacePattern(dllBase, at_pattern, at_replace, 13);
|
|
|
|
// Field of view
|
|
const char *fov_pattern = "\x00\x00\x00\x3F\x17\x6C\xC1\x16\x6C\xC1\x76\x3F";
|
|
char fov_replace[12];
|
|
float fov;
|
|
memcpy(fov_replace, fov_pattern, 12); // Make editable copy of pattern
|
|
memcpy(&fov, fov_replace, sizeof(fov)); // Get float from bytes
|
|
fov *= 1.0f/config.GetFloat(_T("FOVMultiplier")); // Multiply FOV
|
|
memcpy(fov_replace, &fov, sizeof(fov)); // Store back into bytes
|
|
SearchReplacePattern(dllBase, fov_pattern, fov_replace, 12);
|
|
|
|
// FPS Cap
|
|
std::string fps_behavior = config.GetString(_T("FPSLimit"));
|
|
if (fps_behavior != "Default") {
|
|
UINT32 frame_delta;
|
|
|
|
if (fps_behavior == "Limited") {
|
|
frame_delta = 1000.0f / config.GetFloat(_T("CustomFPS"));
|
|
} else {
|
|
frame_delta = 0;
|
|
}
|
|
|
|
WriteMemory((char*)exeBase+0x10B4, &frame_delta, sizeof(UINT32));
|
|
}
|
|
|
|
// Window size hack
|
|
/*SearchReplacePattern(exeBase,
|
|
"\x80\x02\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00",
|
|
"\x40\x01\x00\x00\xE0\x01\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00\x80\x02\x00\x00\xE0\x01\x00\x00", 24);*/
|
|
|
|
return 0;
|
|
}
|
|
|
|
BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved)
|
|
{
|
|
switch (ul_reason_for_call) {
|
|
case DLL_PROCESS_ATTACH:
|
|
// Allocate console
|
|
AllocConsole();
|
|
|
|
// Direct stdin/stderr/stdout to console
|
|
_tfreopen(TEXT("CONIN$"), TEXT("r"), stdin);
|
|
_tfreopen(TEXT("CONOUT$"), TEXT("w"), stderr);
|
|
_tfreopen(TEXT("CONOUT$"), TEXT("w"), stdout);
|
|
|
|
// Print success line
|
|
printf("Injected successfully\n");
|
|
|
|
Patch();
|
|
break;
|
|
}
|
|
|
|
return TRUE;
|
|
}
|