Rewrote ASM code of WorldMapFPSPatch in C++

This commit is contained in:
NovaRain
2021-02-07 21:11:12 +08:00
parent 7c6f9df404
commit fe33dc2834
7 changed files with 98 additions and 92 deletions
+2 -1
View File
@@ -1,5 +1,5 @@
;sfall configuration settings ;sfall configuration settings
;v4.2.9 ;v4.3
[Main] [Main]
;Change to 1 if you want to use command line args to tell sfall to use another ini file. ;Change to 1 if you want to use command line args to tell sfall to use another ini file.
@@ -240,6 +240,7 @@ WorldMapTimeMod=100
WorldMapFPSPatch=1 WorldMapFPSPatch=1
;Controls the world map speed if WorldMapFPSPatch is 1. Higher values cause slower movement ;Controls the world map speed if WorldMapFPSPatch is 1. Higher values cause slower movement
;Default is 66 milliseconds
WorldMapDelay2=66 WorldMapDelay2=66
;Set to 1 to enable Ray's patch to make world map encounter rate independent of your travel speed ;Set to 1 to enable Ray's patch to make world map encounter rate independent of your travel speed
+2 -2
View File
@@ -37,13 +37,13 @@ long Items::item_weapon_mp_cost(fo::GameObject* source, fo::GameObject* weapon,
if (weapon) cost = 2; // default reload AP cost if (weapon) cost = 2; // default reload AP cost
} }
return (cost != -1) ? sf::CalcApCostHook_CheckScript(source, hitMode, isCalled, cost, weapon) : cost; return (cost != -1) ? sf::CalcApCostHook_ScriptCheck(source, hitMode, isCalled, cost, weapon) : cost;
} }
// Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook // Implementation of item_w_mp_cost_ engine function with the HOOK_CALCAPCOST hook
long __fastcall Items::item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled) { long __fastcall Items::item_w_mp_cost(fo::GameObject* source, long hitMode, long isCalled) {
long cost = fo::func::item_w_mp_cost(source, hitMode, isCalled); long cost = fo::func::item_w_mp_cost(source, hitMode, isCalled);
return sf::CalcApCostHook_CheckScript(source, hitMode, isCalled, cost, nullptr); return sf::CalcApCostHook_ScriptCheck(source, hitMode, isCalled, cost, nullptr);
} }
void Items::init() { void Items::init() {
+1 -1
View File
@@ -97,7 +97,7 @@ static long CalcApCostHook_Script(fo::GameObject* source, long hitMode, long isC
return cost; return cost;
} }
long CalcApCostHook_CheckScript(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) { long CalcApCostHook_ScriptCheck(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon) {
return (HookScripts::HookHasScript(HOOK_CALCAPCOST)) return (HookScripts::HookHasScript(HOOK_CALCAPCOST))
? CalcApCostHook_Script(source, hitMode, isCalled, cost, weapon) ? CalcApCostHook_Script(source, hitMode, isCalled, cost, weapon)
: cost; : cost;
+1 -1
View File
@@ -21,6 +21,6 @@ void Inject_TargetObjectHook();
int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds); int __fastcall AmmoCostHook_Script(DWORD hookType, fo::GameObject* weapon, DWORD &rounds);
long CalcApCostHook_CheckScript(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon); long CalcApCostHook_ScriptCheck(fo::GameObject* source, long hitMode, long isCalled, long cost, fo::GameObject* weapon);
} }
+27 -23
View File
@@ -1,20 +1,20 @@
/* /*
* sfall * sfall
* Copyright (C) 2008-2017 The sfall team * Copyright (C) 2008-2017 The sfall team
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#include <cmath> #include <cmath>
@@ -39,8 +39,12 @@ static const DWORD offsets[] = {
0x4F4E53, 0x4F5542, 0x4F56CC, 0x4F59C6, // for mve 0x4F4E53, 0x4F5542, 0x4F56CC, 0x4F59C6, // for mve
}; };
DWORD sf_GetTickCount = (DWORD)&GetTickCount; static DWORD getLocalTimeOffs;
DWORD sf_GetLocalTime; DWORD SpeedPatch::getTickCountOffs = (DWORD)&GetTickCount;
DWORD SpeedPatch::getTickCount() {
return ((DWORD (__stdcall*)())getTickCountOffs)();
}
static bool enabled = true; static bool enabled = true;
static bool toggled = false; static bool toggled = false;
@@ -146,7 +150,7 @@ void TimerInit() {
char buf[2], spKey[10] = "SpeedKey#"; char buf[2], spKey[10] = "SpeedKey#";
char spMulti[12] = "SpeedMulti#"; char spMulti[12] = "SpeedMulti#";
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
_itoa_s(i, buf, 10); _itoa(i, buf, 10);
spKey[8] = spMulti[10] = buf[0]; spKey[8] = spMulti[10] = buf[0];
speed[i].key = GetConfigInt("Input", spKey, 0); speed[i].key = GetConfigInt("Input", spKey, 0);
speed[i].multiplier = GetConfigInt("Speed", spMulti, 0) / 100.0; speed[i].multiplier = GetConfigInt("Speed", spMulti, 0) / 100.0;
@@ -164,16 +168,16 @@ void SpeedPatch::init() {
multi = (double)init / 100.0; multi = (double)init / 100.0;
toggleKey = GetConfigInt("Input", "SpeedToggleKey", 0); toggleKey = GetConfigInt("Input", "SpeedToggleKey", 0);
sf_GetTickCount = (DWORD)&FakeGetTickCount; getTickCountOffs = (DWORD)&FakeGetTickCount;
sf_GetLocalTime = (DWORD)&FakeGetLocalTime; getLocalTimeOffs = (DWORD)&FakeGetLocalTime;
int size = sizeof(offsets) / 4; int size = sizeof(offsets) / 4;
if (GetConfigInt("Speed", "AffectPlayback", 0) == 0) size -= 4; if (GetConfigInt("Speed", "AffectPlayback", 0) == 0) size -= 4;
for (int i = 0; i < size; i++) { for (int i = 0; i < size; i++) {
SafeWrite32(offsets[i], (DWORD)&sf_GetTickCount); SafeWrite32(offsets[i], (DWORD)&getTickCountOffs);
} }
SafeWrite32(0x4FDF58, (DWORD)&sf_GetLocalTime); SafeWrite32(0x4FDF58, (DWORD)&getLocalTimeOffs);
HookCall(0x4A433E, scripts_check_state_hook); HookCall(0x4A433E, scripts_check_state_hook);
TimerInit(); TimerInit();
+21 -18
View File
@@ -1,20 +1,20 @@
/* /*
* sfall * sfall
* Copyright (C) 2008-2017 The sfall team * Copyright (C) 2008-2017 The sfall team
* *
* This program is free software: you can redistribute it and/or modify * 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 * it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or * the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version. * (at your option) any later version.
* *
* This program is distributed in the hope that it will be useful, * This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details. * GNU General Public License for more details.
* *
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>. * along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
#pragma once #pragma once
@@ -28,8 +28,11 @@ public:
const char* name() { return "SpeedPatch"; } const char* name() { return "SpeedPatch"; }
void init(); void init();
void exit() override; void exit() override;
static DWORD getTickCountOffs;
// sfall implementation of the GetTickCount() function
static DWORD getTickCount();
}; };
extern DWORD sf_GetTickCount;
} }
+44 -46
View File
@@ -52,6 +52,7 @@ static bool restMap;
static bool restMode; static bool restMode;
static bool restTime; static bool restTime;
static bool worldMapLongDelay = false;
static DWORD worldMapDelay; static DWORD worldMapDelay;
static DWORD worldMapTicks; static DWORD worldMapTicks;
@@ -130,52 +131,42 @@ end:
} }
} }
static void __declspec(naked) WorldMapFpsPatch() { static void WorldMapFPS() {
DWORD prevTicks = worldMapTicks; // previous ticks
while (true) {
WorldmapLoopHook();
if (worldMapLongDelay) {
__asm call fo::funcoffs::process_bk_;
}
DWORD tick; // current ticks
while (true) {
tick = SpeedPatch::getTickCount();
// get elapsed time
if ((tick - prevTicks) >= 10) break; // delay 10 ms (GetTickCount returns a difference of 10-16 ms)
}
prevTicks = tick;
// get elapsed time
if ((tick - worldMapTicks) >= worldMapDelay) break;
}
worldMapTicks = prevTicks;
}
static void __declspec(naked) wmWorldMap_hook_patch1() {
__asm { __asm {
push dword ptr ds:[FO_VAR_last_buttons]; push dword ptr ds:[FO_VAR_last_buttons];
push dword ptr ds:[0x6AC7B0]; // _mouse_buttons push dword ptr ds:[FO_VAR_mouse_buttons];
mov esi, worldMapTicks; call WorldMapFPS;
mov ebx, esi; // previous ticks pop dword ptr ds:[FO_VAR_mouse_buttons];
loopDelay:
call WorldmapLoopHook;
call fo::funcoffs::process_bk_;
subLoop:
call ds:[sf_GetTickCount]; // current ticks
mov edx, eax;
sub eax, ebx; // get elapsed time (cur.ticks - prev.ticks)
cmp eax, 10; // delay - GetTickCount returns minimum difference of 15 units
jb subLoop; // elapsed < invoke delay
mov ebx, edx;
sub edx, esi; // get elapsed time (cur.ticks - worldMapTicks)
cmp edx, worldMapDelay;
jb loopDelay; // elapsed < worldMapDelay
pop dword ptr ds:[0x6AC7B0]; // _mouse_buttons
pop dword ptr ds:[FO_VAR_last_buttons]; pop dword ptr ds:[FO_VAR_last_buttons];
call ds:[sf_GetTickCount];
mov worldMapTicks, eax;
jmp fo::funcoffs::get_input_; jmp fo::funcoffs::get_input_;
} }
} }
static void __declspec(naked) WorldMapFpsPatch2() { static void __declspec(naked) wmWorldMap_hook_patch2() {
__asm { __asm {
mov esi, worldMapTicks; call WorldMapFPS;
mov ebx, esi;
loopDelay:
call WorldmapLoopHook;
subLoop:
call ds:[sf_GetTickCount]; // current ticks
mov edx, eax;
sub eax, ebx; // get elapsed time
jz subLoop;
mov ebx, edx;
sub edx, esi; // get elapsed time
cmp edx, worldMapDelay;
jb loopDelay; // elapsed < worldMapDelay
call ds:[sf_GetTickCount];
mov worldMapTicks, eax;
jmp fo::funcoffs::get_input_; jmp fo::funcoffs::get_input_;
} }
} }
@@ -183,9 +174,14 @@ subLoop:
// Only used if the world map speed patch is disabled, so that world map scripts are still run // Only used if the world map speed patch is disabled, so that world map scripts are still run
static void __declspec(naked) wmWorldMap_hook() { static void __declspec(naked) wmWorldMap_hook() {
__asm { __asm {
//pushadc; call ds:[SpeedPatch::getTickCountOffs]; // current ticks
mov edx, eax;
sub eax, worldMapTicks; // get elapsed time (cur.ticks - prev.ticks)
cmp eax, 10; // delay 10 ms (GetTickCount returns a difference of 10-16 ms)
jb skipHook;
mov worldMapTicks, edx;
call WorldmapLoopHook; call WorldmapLoopHook;
//popadc; skipHook:
jmp fo::funcoffs::get_input_; jmp fo::funcoffs::get_input_;
} }
} }
@@ -268,6 +264,7 @@ skip:
} }
static const char* automap = "automap"; // no/yes overrides the value in the table to display the automap in pipboy static const char* automap = "automap"; // no/yes overrides the value in the table to display the automap in pipboy
static void __declspec(naked) wmMapInit_hack() { static void __declspec(naked) wmMapInit_hack() {
__asm { __asm {
mov esi, [esp + 0xA0 - 0x20 + 4]; // curent map number mov esi, [esp + 0xA0 - 0x20 + 4]; // curent map number
@@ -401,12 +398,12 @@ static void WorldmapFpsPatch() {
bool fpsPatchOK = (*(DWORD*)0x4BFE5E == 0x8D16); bool fpsPatchOK = (*(DWORD*)0x4BFE5E == 0x8D16);
if (GetConfigInt("Misc", "WorldMapFPSPatch", 0)) { if (GetConfigInt("Misc", "WorldMapFPSPatch", 0)) {
dlog("Applying world map fps patch.", DL_INIT); dlog("Applying world map fps patch.", DL_INIT);
if (!fpsPatchOK) { if (fpsPatchOK) {
dlogr(" Failed", DL_INIT);
} else {
int delay = GetConfigInt("Misc", "WorldMapDelay2", 66); int delay = GetConfigInt("Misc", "WorldMapDelay2", 66);
worldMapDelay = max(1, delay); worldMapDelay = max(1, delay);
dlogr(" Done", DL_INIT); dlogr(" Done", DL_INIT);
} else {
dlogr(" Failed", DL_INIT);
} }
} }
if (fpsPatchOK) { if (fpsPatchOK) {
@@ -414,11 +411,12 @@ static void WorldmapFpsPatch() {
if (worldMapDelay == 0) { if (worldMapDelay == 0) {
func = wmWorldMap_hook; func = wmWorldMap_hook;
} else if (worldMapDelay > 25) { } else if (worldMapDelay > 25) {
func = WorldMapFpsPatch; worldMapLongDelay = true;
func = wmWorldMap_hook_patch1;
} else { } else {
func = WorldMapFpsPatch2; func = wmWorldMap_hook_patch2;
} }
HookCall(0x4BFE5D, func); HookCall(0x4BFE5D, func); // wmWorldMap_
::sfall::availableGlobalScriptTypes |= 2; ::sfall::availableGlobalScriptTypes |= 2;
} }