Files
PDModdingCommunity_PD-Loader/source-code/source/plugins/TLAC/Components/FrameRateManager.cpp
T

182 lines
6.7 KiB
C++

#include "FrameRateManager.h"
#include "../Constants.h"
#include "GameState.h"
#include <stdio.h>
#include <windows.h>
#include "../framework.h"
#include <vector>
#include "detours.h"
namespace TLAC::Components
{
FrameRateManager::FrameRateManager()
{
std::string utf8path = TLAC::framework::GetModuleDirectory() + "/config.ini";
WCHAR utf16buf[256];
MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, utf16buf, 256);
float nMotionRate = GetPrivateProfileIntW(L"graphics", L"frm.motion.rate", 300, utf16buf);
motionSpeedMultiplier = nMotionRate / 60.0f;
}
FrameRateManager::~FrameRateManager()
{
}
const char* FrameRateManager::GetDisplayName()
{
return "frame_rate_manager";
}
float FrameRateManager::fspeed_error = 0; // compensation value for use in this frame
float FrameRateManager::fspeed_error_next = 0; // save a compensation value to be used in the next frame
float FrameRateManager::fspeedhook_lastresult = 0; // used by the ageage hair patch to be lazy and avoid setting up for a proper call
float(*divaGetFrameSpeed)() = (float(*)())0x140192D50;
// a version of the original function that tries to round the output to more closely match chara motion timings
float hookedGetFrameSpeed()
{
float frameSpeed = divaGetFrameSpeed();
// below is somewhat based (in concept) on 140194ad0 (motion quantisation thingy func)
// add the error compensation from last frame
frameSpeed += FrameRateManager::fspeed_error;
// separate whole and fractional parts of speed
// float speed_rounded = floorf(frameSpeed);
// float speed_remainder = frameSpeed - speed_rounded;
float speed_rounded;
float speed_remainder = modff(frameSpeed, &speed_rounded);
// save the remainder as error compensation for next frame
// use == 0 to detect new frame and vars must be written
if (FrameRateManager::fspeed_error_next == 0)
{
FrameRateManager::fspeed_error_next = speed_remainder;
FrameRateManager::fspeedhook_lastresult = speed_rounded;
}
return speed_rounded;
}
void FrameRateManager::Initialize(ComponentsManager*)
{
pvFrameRate = (float*)PV_FRAME_RATE_ADDRESS;
frameSpeed = (float*)FRAME_SPEED_ADDRESS;
aetFrameDuration = (float*)AET_FRAME_DURATION_ADDRESS;
// The default is expected to be 1.0 / 60.0
defaultAetFrameDuration = *aetFrameDuration;
// This const variable is stored inside a data segment so we don't want to throw any access violations
DWORD oldProtect;
VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect);
// fix auto speed for high fps
InjectCode((void*)0x140192d7b, { 0x90, 0x90, 0x90 });
// fix frame speed slider initial value (should ignore effect of auto speed)
InjectCode((void*)0x140338f2f, { 0xf3, 0x0f, 0x10, 0x0d, 0x61, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
InjectCode((void*)0x140338f37, { 0x48, 0x8b, 0x8f, 0x80, 0x01, 0x00, 0x00 }); // MOV RCX, qword ptr [0x180 + RDI]
InjectCode((void*)0x140338ebe, { 0xf3, 0x0f, 0x10, 0x0d, 0xd2, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
InjectCode((void*)0x140338ec6, { 0x48, 0x8b, 0x05, 0xfb, 0xb1, 0xe5, 0x00 }); // MOV RAX, qword ptr [0x1411940c8]
InjectCode((void*)0x140338ecd, { 0x48, 0x8b, 0x88, 0x80, 0x01, 0x00, 0x00 }); // MOV RCX, qword ptr [0x180 + RAX]
// fix AETs
InjectCode((void*)0x140170394, { 0xF3, 0x0F, 0x5E, 0x05, 0x34, 0xA3, 0xD6, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)
// fix edit PV AETs (thanks lyb)
InjectCode((void*)0x140192d30, { 0xF3, 0x0F, 0x10, 0x05, 0x5C, 0x02, 0x00, 0x00 }); // MOVSS XMM0, dword ptr [0x140192f94]
// fix ageage hair effect
InjectCode((void*)0x14054352f, { // MOV R9, &fspeedhook_lastresult
0x49,
0xB9,
(uint8_t)((uint64_t)&fspeedhook_lastresult & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 8) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 16) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 24) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 32) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 40) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 48) & 0xFF),
(uint8_t)(((uint64_t)&fspeedhook_lastresult >> 56) & 0xFF),
});
InjectCode((void*)0x140543539, { 0xF3, 0x41, 0x0F, 0x59, 0x19 }); // MULSS XMM3, dword ptr [R9]
InjectCode((void*)0x14054353e, { 0xEB, 0xB0 }); // JMP 0x1405434f0
// fix wind effect
InjectCode((void*)0x14053ca71, { 0xEB, 0x3F }); // JMP 0x14053cab2
InjectCode((void*)0x14053cab2, { 0xF3, 0x0F, 0x10, 0x05, 0xFA, 0x53, 0x46, 0x00 }); // MOVSS XMM0, dword ptr [0x1409a1eb4] (60.0f)
InjectCode((void*)0x14053caba, { 0xE9, 0x42, 0xFE, 0xFF, 0xFF }); // JMP 0x14053c901
InjectCode((void*)0x14053c901, { 0xF3, 0x0F, 0x5E, 0x05, 0xC7, 0xDD, 0x99, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)
InjectCode((void*)0x14053c909, { 0xE9, 0x68, 0x01, 0x00, 0x00 }); // JMP 0x14053ca76
// replace divaGetFrameSpeed with a version that rounds the output to fix issues
DetourTransactionBegin();
DetourUpdateThread(GetCurrentThread());
DetourAttach(&(PVOID&)divaGetFrameSpeed, hookedGetFrameSpeed);
DetourTransactionCommit();
}
void FrameRateManager::Update()
{
// *aetFrameDuration = 1.0f / GetGameFrameRate();
if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME)
{
*pvFrameRate = 60.0f * motionSpeedMultiplier;
}
else
{
*pvFrameRate = 60.0f;
}
if (*(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_MAIN || *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_DEMO)
{
// enable dynamic framerate
*(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;
// target framerate
*(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = *pvFrameRate;
// trying to fix meltdown's water
//if ((uint64_t*)0x1411943f8 != nullptr)
//*(float*)(*(uint64_t*)0x1411943f8 + 0x1c) = (60.0f / GetGameFrameRate()) / 15.0f;
//*(float*)(*(uint64_t*)0x1411943f8 + 0x10) = *(float*)(*(uint64_t*)0x1411943f8);
}
else
{
// enable dynamic framerate
*(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;
// target framerate
*(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = 60.0f;
}
}
void FrameRateManager::UpdateDraw2D()
{
// cycle the framespeed timing error once per frame
fspeed_error = fspeed_error_next;
fspeed_error_next = 0;
}
void FrameRateManager::InjectCode(void* address, const std::vector<uint8_t> data)
{
const size_t byteCount = data.size() * sizeof(uint8_t);
DWORD oldProtect;
VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
memcpy(address, data.data(), byteCount);
VirtualProtect(address, byteCount, oldProtect, nullptr);
}
}