Files
sfall/sfall/main.cpp
T
NovaRain 22140e2a0c Added performance patches, some code moved to main.cpp
Minor edits to ddraw.ini.

Tweaked the names in AsmMacros.h.
2020-03-22 09:27:24 +08:00

989 lines
30 KiB
C++

/*
* sfall
* Copyright (C) 2008-2016 The sfall team
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#include <algorithm>
#include <math.h>
#include <stdio.h>
#include "main.h"
#include "FalloutEngine.h"
#include "AI.h"
#include "Animations.h"
#include "BarBoxes.h"
#include "Books.h"
#include "BugFixes.h"
#include "BurstMods.h"
#include "Combat.h"
#include "Console.h"
#include "CRC.h"
#include "Credits.h"
#include "Criticals.h"
#include "DamageMod.h"
#include "DebugEditor.h"
#include "Elevators.h"
#include "Explosions.h"
#include "ExtraSaveSlots.h"
#include "FileSystem.h"
#include "Graphics.h"
#include "HeroAppearance.h"
#include "Interface.h"
#include "Inventory.h"
#include "Karma.h"
#include "KillCounter.h"
#include "LoadGameHook.h"
#include "LoadOrder.h"
#include "Logging.h"
#include "MainMenu.h"
#include "Message.h"
#include "Movies.h"
#include "Objects.h"
#include "PartyControl.h"
#include "Perks.h"
#include "Premade.h"
#include "QuestList.h"
#include "Reputations.h"
#include "ScriptExtender.h"
#include "Skills.h"
#include "Sound.h"
#include "SpeedPatch.h"
#include "Stats.h"
#include "TalkingHeads.h"
#include "Tiles.h"
#include "Utils.h"
#include "version.h"
#include "Worldmap.h"
ddrawDll ddraw;
bool isDebug = false;
bool hrpIsEnabled = false;
bool hrpVersionValid = false; // HRP 4.1.8 version validation
const char ddrawIniDef[] = ".\\ddraw.ini";
static char ini[65] = ".\\";
static char translationIni[65];
DWORD modifiedIni;
DWORD hrpDLLBaseAddr = 0x10000000;
DWORD HRPAddress(DWORD addr) {
return (hrpDLLBaseAddr + (addr & 0xFFFFF));
}
int iniGetInt(const char* section, const char* setting, int defaultValue, const char* iniFile) {
return GetPrivateProfileIntA(section, setting, defaultValue, iniFile);
}
size_t iniGetString(const char* section, const char* setting, const char* defaultValue, char* buf, size_t bufSize, const char* iniFile) {
return GetPrivateProfileStringA(section, setting, defaultValue, buf, bufSize, iniFile);
}
std::string GetIniString(const char* section, const char* setting, const char* defaultValue, size_t bufSize, const char* iniFile) {
char* buf = new char[bufSize];
iniGetString(section, setting, defaultValue, buf, bufSize, iniFile);
std::string str(buf);
delete[] buf;
return str;
}
std::vector<std::string> GetIniList(const char* section, const char* setting, const char* defaultValue, size_t bufSize, char delimiter, const char* iniFile) {
auto list = split(GetIniString(section, setting, defaultValue, bufSize, iniFile), delimiter);
std::transform(list.cbegin(), list.cend(), list.begin(), trim);
return list;
}
/*
For ddraw.ini config
*/
unsigned int GetConfigInt(const char* section, const char* setting, int defaultValue) {
return iniGetInt(section, setting, defaultValue, ini);
}
std::string GetConfigString(const char* section, const char* setting, const char* defaultValue, size_t bufSize) {
return trim(GetIniString(section, setting, defaultValue, bufSize, ini));
}
size_t GetConfigString(const char* section, const char* setting, const char* defaultValue, char* buf, size_t bufSize) {
return iniGetString(section, setting, defaultValue, buf, bufSize, ini);
}
std::vector<std::string> GetConfigList(const char* section, const char* setting, const char* defaultValue, size_t bufSize) {
return GetIniList(section, setting, defaultValue, bufSize, ',', ini);
}
std::string Translate(const char* section, const char* setting, const char* defaultValue, size_t bufSize) {
return GetIniString(section, setting, defaultValue, bufSize, translationIni);
}
size_t Translate(const char* section, const char* setting, const char* defaultValue, char* buffer, size_t bufSize) {
return iniGetString(section, setting, defaultValue, buffer, bufSize, translationIni);
}
int SetConfigInt(const char* section, const char* setting, int value) {
char* buf = new char[128];
_itoa_s(value, buf, 128, 10);
int result = WritePrivateProfileStringA(section, setting, buf, ini);
delete[] buf;
return result;
}
static char mapName[65] = {};
static char configName[65] = {};
static char patchName[65] = {};
static char versionString[65] = {};
static char startMaleModelName[65] = {};
char defaultMaleModelName[65] = {};
static char startFemaleModelName[65] = {};
char defaultFemaleModelName[65] = {};
static const char* musicOverridePath = "data\\sound\\music\\";
static int* scriptDialog = nullptr;
static const DWORD script_dialog_msgs[] = {
0x4A50C2, 0x4A5169, 0x4A52FA, 0x4A5302, 0x4A6B86, 0x4A6BE0, 0x4A6C37,
};
static const DWORD EncounterTableSize[] = {
0x4BD1A3, 0x4BD1D9, 0x4BD270, 0x4BD604, 0x4BDA14, 0x4BDA44, 0x4BE707,
0x4C0815, 0x4C0D4A, 0x4C0FD4,
};
static const DWORD PutAwayWeapon[] = {
0x411EA2, // action_climb_ladder_
0x412046, // action_use_an_item_on_object_
0x41224A, // action_get_an_object_
0x4606A5, // intface_change_fid_animate_
0x472996, // invenWieldFunc_
};
static const DWORD WalkDistanceAddr[] = {
0x411FF0, 0x4121C4, 0x412475, 0x412906,
};
static void __declspec(naked) WeaponAnimHook() {
__asm {
cmp edx, 11;
je c11;
cmp edx, 15;
je c15;
jmp art_get_code_;
c11:
mov edx, 16;
jmp art_get_code_;
c15:
mov edx, 17;
jmp art_get_code_;
}
}
static void __declspec(naked) intface_item_reload_hook() {
__asm {
push eax;
mov eax, dword ptr ds:[_obj_dude];
call register_clear_;
test eax, eax;
jnz fail;
inc eax;
call register_begin_;
xor edx, edx;
xor ebx, ebx;
mov eax, dword ptr ds:[_obj_dude];
dec ebx;
call register_object_animate_;
call register_end_;
fail:
pop eax;
jmp gsound_play_sfx_file_;
}
}
static void __declspec(naked) action_use_skill_on_hook_science() {
__asm {
cmp esi, ds:[_obj_dude];
jne end;
mov eax, KILL_TYPE_robot;
retn;
end:
jmp critter_kill_count_type_;
}
}
static const DWORD ScannerHookRet = 0x41BC1D;
static const DWORD ScannerHookFail = 0x41BC65;
static void __declspec(naked) automap_hack() {
__asm {
mov eax, ds:[_obj_dude];
mov edx, PID_MOTION_SENSOR;
call inven_pid_is_carried_ptr_;
test eax, eax;
jz fail;
mov edx, eax;
jmp ScannerHookRet;
fail:
jmp ScannerHookFail;
}
}
static void __declspec(naked) op_obj_can_see_obj_hook() {
__asm {
push obj_shoot_blocking_at_; // check hex objects func pointer
push 0x20; // flags, 0x20 = check ShootThru
mov ecx, dword ptr [esp + 0x0C]; // buf **ret_objStruct
push ecx;
xor ecx, ecx;
call make_straight_path_func_; // (EAX *objStruct, EDX hexNum1, EBX hexNum2, ECX 0, stack1 **ret_objStruct, stack2 flags, stack3 *check_hex_objs_func)
retn 8;
}
}
static void __declspec(naked) register_object_take_out_hack() {
__asm {
push ecx;
push eax;
mov ecx, edx; // ID1
mov edx, [eax + 0x1C]; // cur_rot
inc edx;
push edx; // ID3
xor ebx, ebx; // ID2
mov edx, [eax + 0x20]; // fid
and edx, 0xFFF; // Index
xor eax, eax;
inc eax; // Obj_Type CRITTER
call art_id_;
mov edx, eax;
xor ebx, ebx;
dec ebx; // delay -1
pop eax; // critter
call register_object_change_fid_;
pop ecx;
xor eax, eax;
retn;
}
}
static void __declspec(naked) gdAddOptionStr_hack() {
__asm {
mov ecx, ds:[_gdNumOptions];
add ecx, '1';
push ecx;
mov ecx, 0x4458FA;
jmp ecx;
}
}
static DWORD __fastcall GetWeaponSlotMode(DWORD itemPtr, DWORD mode) {
int slot = (mode > 0) ? 1 : 0;
DWORD* itemButton = ptr_itemButtonItems;
if (itemButton[slot * 6] == itemPtr) {
int slotMode = itemButton[(slot * 6) + 4];
if (slotMode == 3 || slotMode == 4) {
mode++;
}
}
return mode;
}
static void __declspec(naked) display_stats_hook() {
__asm {
push eax;
push ecx;
mov ecx, ds:[esp + edi + 0xA8 + 0xC]; // get itemPtr
call GetWeaponSlotMode; // ecx - itemPtr, edx - mode;
mov edx, eax;
pop ecx;
pop eax;
jmp item_w_range_;
}
}
static void __declspec(naked) endgame_movie_hook() {
__asm {
cmp [esp + 16], 0x45C563; // call from op_endgame_movie_
je playWalkMovie;
retn;
playWalkMovie:
call stat_level_;
xor edx, edx;
add eax, 10;
mov ecx, eax;
mov eax, 1500;
call pause_for_tocks_;
mov eax, ecx;
jmp gmovie_play_;
}
}
static void __declspec(naked) op_display_msg_hook() {
__asm {
cmp dword ptr ds:_debug_func, 0;
jne debug;
retn;
debug:
jmp config_get_value_;
}
}
static void DllMain2() {
long tmp;
dlogr("In DllMain2", DL_MAIN);
dlogr("Running BugFixesInit().", DL_INIT);
BugFixesInit();
dlogr("Running GraphicsInit().", DL_INIT);
GraphicsInit();
//if (GetConfigInt("Input", "Enable", 0)) {
dlog("Applying input patch.", DL_INIT);
SafeWriteStr(0x50FB70, "ddraw.dll");
availableGlobalScriptTypes |= 1;
dlogr(" Done", DL_INIT);
//}
dlogr("Running LoadOrderInit().", DL_INIT);
LoadOrderInit();
dlogr("Running LoadGameHookInit().", DL_INIT);
LoadGameHookInit();
dlogr("Running MoviesInit().", DL_INIT);
MoviesInit();
dlog("Applying main menu patches.", DL_INIT);
MainMenuInit();
dlogr(" Done", DL_INIT);
dlogr("Running InterfaceInit().", DL_INIT);
InterfaceInit();
dlogr("Running ObjectsInit().", DL_INIT);
ObjectsInit();
dlogr("Running SpeedPatchInit().", DL_INIT);
SpeedPatchInit();
if (GetConfigString("Misc", "MaleStartModel", "", startMaleModelName, 64)) {
dlog("Applying male start model patch.", DL_INIT);
SafeWrite32(0x418B88, (DWORD)&startMaleModelName);
dlogr(" Done", DL_INIT);
}
if (GetConfigString("Misc", "FemaleStartModel", "", startFemaleModelName, 64)) {
dlog("Applying female start model patch.", DL_INIT);
SafeWrite32(0x418BAB, (DWORD)&startFemaleModelName);
dlogr(" Done", DL_INIT);
}
GetConfigString("Misc", "MaleDefaultModel", "hmjmps", defaultMaleModelName, 64);
dlog("Applying male model patch.", DL_INIT);
SafeWrite32(0x418B50, (DWORD)&defaultMaleModelName);
dlogr(" Done", DL_INIT);
GetConfigString("Misc", "FemaleDefaultModel", "hfjmps", defaultFemaleModelName, 64);
dlog("Applying female model patch.", DL_INIT);
SafeWrite32(0x418B6D, (DWORD)&defaultFemaleModelName);
dlogr(" Done", DL_INIT);
dlogr("Running WorldmapInit().", DL_INIT);
WorldmapInit();
dlogr("Running StatsInit().", DL_INIT);
StatsInit();
dlogr("Running PerksInit().", DL_INIT);
PerksInit();
dlogr("Running CombatInit().", DL_INIT);
CombatInit();
dlogr("Running SkillsInit().", DL_INIT);
SkillsInit();
dlogr("Running FileSystemInit().", DL_INIT);
FileSystemInit();
dlogr("Running CriticalsInit().", DL_INIT);
CriticalsInit();
dlogr("Running KarmaInit().", DL_INIT);
KarmaInit();
dlogr("Running TilesInit().", DL_INIT);
TilesInit();
dlogr("Running CreditsInit().", DL_INIT);
CreditsInit();
dlogr("Running QuestListInit().", DL_INIT);
QuestListInit();
dlogr("Running PremadeInit().", DL_INIT);
PremadeInit();
dlogr("Running SoundInit().", DL_INIT);
SoundInit();
dlogr("Running ReputationsInit().", DL_INIT);
ReputationsInit();
dlogr("Running ConsoleInit().", DL_INIT);
ConsoleInit();
dlogr("Running ExtraSaveSlotsInit().", DL_INIT);
ExtraSaveSlotsInit();
dlogr("Running InventoryInit().", DL_INIT);
InventoryInit();
dlogr("Initializing party control...", DL_INIT);
PartyControlInit();
dlogr("Running BurstModsInit().", DL_INIT);
BurstModsInit();
dlogr("Running BooksInit().", DL_INIT);
BooksInit();
dlogr("Running ExplosionInit().", DL_INIT);
ExplosionInit();
std::string elevPath = GetConfigString("Misc", "ElevatorsFile", "", MAX_PATH);
if (!elevPath.empty()) {
dlog("Applying elevator patch.", DL_INIT);
ElevatorsInit();
LoadElevators(elevPath.insert(0, ".\\").c_str());
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "ExtraKillTypes", 0)) {
dlog("Applying extra kill types patch.", DL_INIT);
KillCounterInit();
dlogr(" Done", DL_INIT);
}
dlogr("Running AIInit().", DL_INIT);
AIInit();
dlogr("Running DamageModInit().", DL_INIT);
DamageModInit();
dlogr("Running AnimationsInit().", DL_INIT);
AnimationsInit();
dlogr("Running BarBoxesInit().", DL_INIT);
BarBoxesInit();
dlogr("Running HeroAppearanceModInit().", DL_INIT);
HeroAppearanceModInit();
/////////////////// Engine Optimization Patches ////////////////////
// Speed up display_msg script function
HookCall(0x455404, op_display_msg_hook);
// Remove duplicate code from intface_redraw_ engine function
BlockCall(0x45EBBF);
// Improve performance of the data conversion of script interpreter
// mov eax, [edx+eax]; bswap eax; ret;
SafeWrite32(0x4672A4, 0x0F02048B);
SafeWrite16(0x4672A8, 0xC3C8);
// mov eax, [edx+eax]; bswap eax;
SafeWrite32(0x4673E5, 0x0F02048B);
SafeWrite8(0x4673E9, 0xC8);
// mov ax, [eax]; rol ax, 8; ret;
SafeWrite32(0x467292, 0x66008B66);
SafeWrite32(0x467296, 0xC308C0C1);
////////////////////////////////////////////////////////////////////
if (GetConfigString("Misc", "StartingMap", "", mapName, 64)) {
dlog("Applying starting map patch.", DL_INIT);
SafeWrite32(0x480AAA, (DWORD)&mapName);
dlogr(" Done", DL_INIT);
}
if (GetConfigString("Misc", "VersionString", "", versionString, 64)) {
dlog("Applying version string patch.", DL_INIT);
SafeWrite32(0x4B4588, (DWORD)&versionString);
dlogr(" Done", DL_INIT);
}
if (GetConfigString("Misc", "ConfigFile", "", configName, 64)) {
dlog("Applying config file patch.", DL_INIT);
SafeWrite32(0x444BA5, (DWORD)&configName);
SafeWrite32(0x444BCA, (DWORD)&configName);
dlogr(" Done", DL_INIT);
}
if (GetConfigString("Misc", "PatchFile", "", patchName, 64)) {
dlog("Applying patch file patch.", DL_INIT);
SafeWrite32(0x444323, (DWORD)&patchName);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "SingleCore", 1)) {
dlog("Applying single core patch.", DL_INIT);
HANDLE process = GetCurrentProcess();
SetProcessAffinityMask(process, 1);
CloseHandle(process);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "OverrideArtCacheSize", 0)) {
dlog("Applying override art cache size patch.", DL_INIT);
SafeWrite32(0x418867, 0x90909090);
SafeWrite32(0x418872, 256);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "Fallout1Behavior", 0)) {
dlog("Applying Fallout 1 engine behavior patch.", DL_INIT);
BlockCall(0x4A4343); // disable playing the final movie/credits after the endgame slideshow
SafeWrite8(0x477C71, 0xEB); // disable halving the weight for power armor items
HookCall(0x43F872, endgame_movie_hook); // play movie 10 or 11 based on the player's gender before the credits
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "DialogueFix", 1)) {
dlog("Applying dialogue patch.", DL_INIT);
SafeWrite8(0x446848, 0x31);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "AdditionalWeaponAnims", 0)) {
dlog("Applying additional weapon animations patch.", DL_INIT);
SafeWrite8(0x419320, 18); // art_get_code_
HookCall(0x4194CC, WeaponAnimHook); // art_get_name_
HookCall(0x451648, WeaponAnimHook); // gsnd_build_character_sfx_name_
HookCall(0x451671, WeaponAnimHook);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "AlwaysReloadMsgs", 0)) {
dlog("Applying always reload messages patch.", DL_INIT);
SafeWrite8(0x4A6B8D, 0x0);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "PlayIdleAnimOnReload", 0)) {
dlog("Applying idle anim on reload patch.", DL_INIT);
HookCall(0x460B8C, intface_item_reload_hook);
dlogr(" Done", DL_INIT);
}
dlog("Checking for changed skilldex images.", DL_INIT);
tmp = GetConfigInt("Misc", "Lockpick", 293);
if (tmp != 293) SafeWrite32(0x518D54, tmp);
tmp = GetConfigInt("Misc", "Steal", 293);
if (tmp != 293) SafeWrite32(0x518D58, tmp);
tmp = GetConfigInt("Misc", "Traps", 293);
if (tmp != 293) SafeWrite32(0x518D5C, tmp);
tmp = GetConfigInt("Misc", "FirstAid", 293);
if (tmp != 293) SafeWrite32(0x518D4C, tmp);
tmp = GetConfigInt("Misc", "Doctor", 293);
if (tmp != 293) SafeWrite32(0x518D50, tmp);
tmp = GetConfigInt("Misc", "Science", 293);
if (tmp != 293) SafeWrite32(0x518D60, tmp);
tmp = GetConfigInt("Misc", "Repair", 293);
if (tmp != 293) SafeWrite32(0x518D64, tmp);
dlogr(" Done", DL_INIT);
if (GetConfigInt("Misc", "RemoveWindowRounding", 1)) {
SafeWrite8(0x4D6EDD, 0xEB);
SafeWrite8(0x4D6F12, 0xEB);
//SafeWrite16(0x4B8090, 0x04EB); // jmps 0x4B8096 (old)
}
switch (GetConfigInt("Misc", "ScienceOnCritters", 0)) {
case 1:
HookCall(0x41276E, action_use_skill_on_hook_science);
break;
case 2:
SafeWrite8(0x41276A, 0xEB);
break;
}
tmp = GetConfigInt("Misc", "SpeedInventoryPCRotation", 166);
if (tmp != 166 && tmp <= 1000) {
dlog("Applying SpeedInventoryPCRotation patch.", DL_INIT);
SafeWrite32(0x47066B, tmp);
dlogr(" Done", DL_INIT);
}
dlogr("Patching out ereg call.", DL_INIT);
BlockCall(0x4425E6);
if (tmp = GetConfigInt("Sound", "OverrideMusicDir", 0)) {
SafeWrite32(0x4449C2, (DWORD)musicOverridePath);
SafeWrite32(0x4449DB, (DWORD)musicOverridePath);
if (tmp == 2) {
SafeWrite32(0x518E78, (DWORD)musicOverridePath);
SafeWrite32(0x518E7C, (DWORD)musicOverridePath);
}
}
if (GetConfigInt("Misc", "BoostScriptDialogLimit", 0)) {
const int scriptDialogCount = 10000;
dlog("Applying script dialog limit patch.", DL_INIT);
scriptDialog = new int[scriptDialogCount * 2]; // Because the msg structure is 8 bytes, not 4.
SafeWrite32(0x4A50E3, scriptDialogCount); // scr_init
SafeWrite32(0x4A519F, scriptDialogCount); // scr_game_init
SafeWrite32(0x4A534F, scriptDialogCount * 8); // scr_message_free
for (int i = 0; i < sizeof(script_dialog_msgs) / 4; i++) {
SafeWrite32(script_dialog_msgs[i], (DWORD)scriptDialog); // scr_get_dialog_msg_file
}
dlogr(" Done", DL_INIT);
}
if (tmp = GetConfigInt("Misc", "MotionScannerFlags", 1)) {
dlog("Applying MotionScannerFlags patch.", DL_INIT);
if (tmp & 1) MakeJump(0x41BBE9, automap_hack);
if (tmp & 2) {
// automap_
SafeWrite16(0x41BC24, 0x9090);
BlockCall(0x41BC3C);
// item_m_use_charged_item_
SafeWrite8(0x4794B3, 0x5E); // jbe short 0x479512
}
dlogr(" Done", DL_INIT);
}
tmp = GetConfigInt("Misc", "EncounterTableSize", 0);
if (tmp > 40 && tmp <= 127) {
dlog("Applying EncounterTableSize patch.", DL_INIT);
SafeWrite8(0x4BDB17, (BYTE)tmp);
DWORD nsize = (tmp + 1) * 180 + 0x50;
for (int i = 0; i < sizeof(EncounterTableSize) / 4; i++) {
SafeWrite32(EncounterTableSize[i], nsize);
}
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "DisablePipboyAlarm", 0)) {
dlog("Applying Disable Pip-Boy alarm button patch.", DL_INIT);
SafeWrite8(0x499518, 0xC3);
SafeWrite8(0x443601, 0x0);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "ObjCanSeeObj_ShootThru_Fix", 0)) {
dlog("Applying ObjCanSeeObj ShootThru Fix.", DL_INIT);
HookCall(0x456BC6, op_obj_can_see_obj_hook);
dlogr(" Done", DL_INIT);
}
// phobos2077:
DWORD addrs[] = {
0x45F9DE, 0x45FB33,
0x447DF4, 0x447EB6,
0x499B99, 0x499DA8
};
SimplePatch<WORD>(addrs, 2, "Misc", "CombatPanelAnimDelay", 1000, 0, 65535);
SimplePatch<BYTE>(&addrs[2], 2, "Misc", "DialogPanelAnimDelay", 33, 0, 255);
SimplePatch<BYTE>(&addrs[4], 2, "Misc", "PipboyTimeAnimDelay", 50, 0, 127);
if (GetConfigInt("Misc", "EnableMusicInDialogue", 0)) {
dlog("Applying music in dialogue patch.", DL_INIT);
SafeWrite8(0x44525B, 0);
//BlockCall(0x450627);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "DontTurnOffSneakIfYouRun", 0)) {
dlog("Applying DontTurnOffSneakIfYouRun patch.", DL_INIT);
SafeWrite8(0x418135, 0xEB);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "InstantWeaponEquip", 0)) {
//Skip weapon equip/unequip animations
dlog("Applying instant weapon equip patch.", DL_INIT);
for (int i = 0; i < sizeof(PutAwayWeapon) / 4; i++) {
SafeWrite8(PutAwayWeapon[i], 0xEB); // jmps
}
BlockCall(0x472AD5); //
BlockCall(0x472AE0); // invenUnwieldFunc_
BlockCall(0x472AF0); //
MakeJump(0x415238, register_object_take_out_hack);
dlogr(" Done", DL_INIT);
}
if (GetConfigInt("Misc", "NumbersInDialogue", 0)) {
dlog("Applying numbers in dialogue patch.", DL_INIT);
SafeWrite32(0x502C32, 0x2000202E);
SafeWrite8(0x446F3B, 0x35);
SafeWrite32(0x5029E2, 0x7325202E);
SafeWrite32(0x446F03, 0x2424448B); // mov eax, [esp+0x24]
SafeWrite8(0x446F07, 0x50); // push eax
SafeWrite32(0x446FE0, 0x2824448B); // mov eax, [esp+0x28]
SafeWrite8(0x446FE4, 0x50); // push eax
MakeJump(0x4458F5, gdAddOptionStr_hack);
dlogr(" Done", DL_INIT);
}
// Display the range of the second attack mode in the inventory when you switch weapon modes in active item slots
//if (GetConfigInt("Misc", "DisplaySecondWeaponRange", 1)) {
dlog("Applying display second weapon range patch.", DL_INIT);
HookCall(0x472201, display_stats_hook);
dlogr(" Done", DL_INIT);
//}
if (GetConfigInt("Misc", "InterfaceDontMoveOnTop", 0)) {
dlog("Applying no MoveOnTop flag for interface patch.", DL_INIT);
SafeWrite8(0x46ECE9, WIN_Exclusive); // Player Inventory/Loot/UseOn
SafeWrite8(0x41B966, WIN_Exclusive); // Automap
dlogr(" Done", DL_INIT);
}
int distance = GetConfigInt("Misc", "UseWalkDistance", 3) + 2;
if (distance > 1 && distance < 5) {
dlog("Applying walk distance for using objects patch.", DL_INIT);
for (int i = 0; i < sizeof(WalkDistanceAddr) / 4; i++) {
SafeWrite8(WalkDistanceAddr[i], static_cast<BYTE>(distance)); // default is 5
}
dlogr(" Done", DL_INIT);
}
if (int time = GetConfigInt("Misc", "CorpseDeleteTime", 6) != 6) { // time in days
dlog("Applying corpse deletion time patch.", DL_INIT);
if (time <= 0) {
time = 12; // hours
} else if (time > 13) {
time = 13 * 24;
} else {
time *= 24;
}
SafeWrite32(0x483348, time);
dlogr(" Done", DL_INIT);
}
SimplePatch<DWORD>(0x440C2A, "Misc", "SpecialDeathGVAR", 491); // GVAR_MODOC_SHITTY_DEATH
// Remove hardcoding for maps with IDs 19 and 37
if (GetConfigInt("Misc", "DisableSpecialMapIDs", 0)) {
dlog("Applying disable special map IDs patch.", DL_INIT);
SafeWrite8(0x4836D6, 0);
SafeWrite8(0x4836DB, 0);
dlogr(" Done", DL_INIT);
}
// Increase the max text width of the information card in the character screen
SafeWrite8(0x43ACD5, 145); // 136
SafeWrite8(0x43DD37, 145); // 133
dlogr("Running TalkingHeadsInit().", DL_INIT);
TalkingHeadsInit();
// most of modules should be initialized before running the script handlers
dlogr("Running ScriptExtenderInit().", DL_INIT);
ScriptExtenderInit();
dlogr("Running DebugEditorInit().", DL_INIT);
DebugEditorInit();
dlogr("Leave DllMain2", DL_MAIN);
}
static void _stdcall OnExit() {
if (scriptDialog) delete[] scriptDialog;
GraphicsExit();
InterfaceExit();
SpeedPatchExit();
SkillsExit();
SoundExit();
ReputationsExit();
ConsoleExit();
ExtraSaveSlotsExit();
BooksExit();
//gExtraGameMsgLists.clear();
AnimationsExit();
BarBoxesExit();
HeroAppearanceModExit();
TalkingHeadsExit();
}
static void __declspec(naked) OnExitFunc() {
__asm {
pushad;
call OnExit;
popad;
jmp DOSCmdLineDestroy_;
}
}
static const DWORD loadFunc = 0x4FE1D0;
static void LoadHRPModule() {
HMODULE dll;
__asm call loadFunc; // get HRP loading address
__asm mov dll, eax;
if (dll != NULL) hrpDLLBaseAddr = (DWORD)dll;
dlog_f("Loaded f2_res.dll library at the memory address: 0x%x\n", DL_MAIN, dll);
}
static void CompatModeCheck(HKEY root, const char* filepath, int extra) {
HKEY key;
char buf[MAX_PATH];
DWORD size = MAX_PATH;
DWORD type;
if (!(type = RegOpenKeyEx(root, "Software\\Microsoft\\Windows NT\\CurrentVersion\\AppCompatFlags\\Layers", 0, extra | STANDARD_RIGHTS_READ | KEY_QUERY_VALUE, &key))) {
if (!RegQueryValueEx(key, filepath, 0, &type, (BYTE*)buf, &size)) {
if (size && (type == REG_EXPAND_SZ || type == REG_MULTI_SZ || type == REG_SZ)) {
if (strstr(buf, "256COLOR") || strstr(buf, "640X480") || strstr(buf, "WIN")) {
RegCloseKey(key);
MessageBoxA(0, "Fallout appears to be running in compatibility mode.\n" //, and sfall was not able to disable it.\n"
"Please check the compatibility tab of fallout2.exe, and ensure that the following settings are unchecked:\n"
"Run this program in compatibility mode for..., run in 256 colours, and run in 640x480 resolution.\n"
"If these options are disabled, click the 'change settings for all users' button and see if that enables them.", "Error", MB_TASKMODAL | MB_ICONERROR);
ExitProcess(-1);
}
}
}
RegCloseKey(key);
}
}
static bool LoadOriginalDll(DWORD dwReason) {
switch (dwReason) {
case DLL_PROCESS_ATTACH:
char path[MAX_PATH];
CopyMemory(path + GetSystemDirectoryA(path , MAX_PATH - 10), "\\ddraw.dll", 11); // path to original dll
ddraw.dll = LoadLibraryA(path);
if (ddraw.dll) {
ddraw.AcquireDDThreadLock = GetProcAddress(ddraw.dll, "AcquireDDThreadLock");
ddraw.CheckFullscreen = GetProcAddress(ddraw.dll, "CheckFullscreen");
ddraw.CompleteCreateSysmemSurface = GetProcAddress(ddraw.dll, "CompleteCreateSysmemSurface");
ddraw.D3DParseUnknownCommand = GetProcAddress(ddraw.dll, "D3DParseUnknownCommand");
ddraw.DDGetAttachedSurfaceLcl = GetProcAddress(ddraw.dll, "DDGetAttachedSurfaceLcl");
ddraw.DDInternalLock = GetProcAddress(ddraw.dll, "DDInternalLock");
ddraw.DDInternalUnlock = GetProcAddress(ddraw.dll, "DDInternalUnlock");
ddraw.DSoundHelp = GetProcAddress(ddraw.dll, "DSoundHelp");
ddraw.DirectDrawCreateClipper = GetProcAddress(ddraw.dll, "DirectDrawCreateClipper");
ddraw.DirectDrawCreate = GetProcAddress(ddraw.dll, "DirectDrawCreate");
ddraw.DirectDrawCreateEx = GetProcAddress(ddraw.dll, "DirectDrawCreateEx");
ddraw.DirectDrawEnumerateA = GetProcAddress(ddraw.dll, "DirectDrawEnumerateA");
ddraw.DirectDrawEnumerateExA = GetProcAddress(ddraw.dll, "DirectDrawEnumerateExA");
ddraw.DirectDrawEnumerateExW = GetProcAddress(ddraw.dll, "DirectDrawEnumerateExW");
ddraw.DirectDrawEnumerateW = GetProcAddress(ddraw.dll, "DirectDrawEnumerateW");
ddraw.DllCanUnloadNow = GetProcAddress(ddraw.dll, "DllCanUnloadNow");
ddraw.DllGetClassObject = GetProcAddress(ddraw.dll, "DllGetClassObject");
ddraw.GetDDSurfaceLocal = GetProcAddress(ddraw.dll, "GetDDSurfaceLocal");
ddraw.GetOLEThunkData = GetProcAddress(ddraw.dll, "GetOLEThunkData");
ddraw.GetSurfaceFromDC = GetProcAddress(ddraw.dll, "GetSurfaceFromDC");
ddraw.RegisterSpecialCase = GetProcAddress(ddraw.dll, "RegisterSpecialCase");
ddraw.ReleaseDDThreadLock = GetProcAddress(ddraw.dll, "ReleaseDDThreadLock");
ddraw.SetAppCompatData = GetProcAddress(ddraw.dll, "SetAppCompatData");
}
return true;
case DLL_PROCESS_DETACH:
if (ddraw.dll) FreeLibrary(ddraw.dll);
break;
}
return false;
}
bool _stdcall DllMain(HANDLE hDllHandle, DWORD dwReason, LPVOID lpreserved) {
if (LoadOriginalDll(dwReason)) {
// enabling debugging features
isDebug = (iniGetInt("Debugging", "Enable", 0, ddrawIniDef) != 0);
if (isDebug) {
LoggingInit();
if (!ddraw.dll) dlog("Error: Cannot load the original ddraw.dll library.\n", DL_MAIN);
}
HookCall(0x4DE7D2, &OnExitFunc);
char filepath[MAX_PATH];
GetModuleFileName(0, filepath, MAX_PATH);
CRC(filepath);
if (!isDebug || !iniGetInt("Debugging", "SkipCompatModeCheck", 0, ddrawIniDef)) {
int is64bit;
typedef int (_stdcall *chk64bitproc)(HANDLE, int*);
HMODULE h = LoadLibrary("Kernel32.dll");
chk64bitproc proc = (chk64bitproc)GetProcAddress(h, "IsWow64Process");
if (proc)
proc(GetCurrentProcess(), &is64bit);
else
is64bit = 0;
FreeLibrary(h);
CompatModeCheck(HKEY_CURRENT_USER, filepath, is64bit ? KEY_WOW64_64KEY : 0);
CompatModeCheck(HKEY_LOCAL_MACHINE, filepath, is64bit ? KEY_WOW64_64KEY : 0);
}
// ini file override
bool cmdlineexists = false;
char* cmdline = GetCommandLineA();
if (iniGetInt("Main", "UseCommandLine", 0, ddrawIniDef)) {
while (cmdline[0] == ' ') cmdline++;
bool InQuote = false;
int count = -1;
while (true) {
count++;
if (cmdline[count] == 0) break;;
if (cmdline[count] == ' ' && !InQuote) break;
if (cmdline[count] == '"') {
InQuote = !InQuote;
if (!InQuote) break;
}
}
if (cmdline[count] != 0) {
count++;
while (cmdline[count] == ' ') count++;
cmdline = &cmdline[count];
cmdlineexists = true;
}
}
if (cmdlineexists && *cmdline != 0) {
HANDLE h = CreateFileA(cmdline, GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0);
if (h != INVALID_HANDLE_VALUE) {
CloseHandle(h);
strcat_s(ini, cmdline);
} else {
MessageBoxA(0, "You gave a command line argument to Fallout, but it couldn't be matched to a file.\n" \
"Using default ddraw.ini instead.", "Warning", MB_TASKMODAL | MB_ICONWARNING);
goto defaultIni;
}
} else {
defaultIni:
strcpy(&ini[2], &ddrawIniDef[2]);
}
GetConfigString("Main", "TranslationsINI", ".\\Translations.ini", translationIni, 65);
modifiedIni = GetConfigInt("Main", "ModifiedIni", 0);
hrpIsEnabled = (*(DWORD*)0x4E4480 != 0x278805C7); // check if HRP is enabled
if (hrpIsEnabled) {
LoadHRPModule();
if (strncmp((const char*)HRPAddress(0x10039940), "4.1.8", 5) == 0) hrpVersionValid = true;
}
DllMain2();
}
return true;
}