123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- #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);
- }
- }
|