FrameRateManager.cpp 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182
  1. #include "FrameRateManager.h"
  2. #include "../Constants.h"
  3. #include "GameState.h"
  4. #include <stdio.h>
  5. #include <windows.h>
  6. #include "../framework.h"
  7. #include <vector>
  8. #include "detours.h"
  9. namespace TLAC::Components
  10. {
  11. FrameRateManager::FrameRateManager()
  12. {
  13. std::string utf8path = TLAC::framework::GetModuleDirectory() + "/config.ini";
  14. WCHAR utf16buf[256];
  15. MultiByteToWideChar(CP_UTF8, 0, utf8path.c_str(), -1, utf16buf, 256);
  16. float nMotionRate = GetPrivateProfileIntW(L"graphics", L"frm.motion.rate", 300, utf16buf);
  17. motionSpeedMultiplier = nMotionRate / 60.0f;
  18. }
  19. FrameRateManager::~FrameRateManager()
  20. {
  21. }
  22. const char* FrameRateManager::GetDisplayName()
  23. {
  24. return "frame_rate_manager";
  25. }
  26. float FrameRateManager::fspeed_error = 0; // compensation value for use in this frame
  27. float FrameRateManager::fspeed_error_next = 0; // save a compensation value to be used in the next frame
  28. float FrameRateManager::fspeedhook_lastresult = 0; // used by the ageage hair patch to be lazy and avoid setting up for a proper call
  29. float(*divaGetFrameSpeed)() = (float(*)())0x140192D50;
  30. // a version of the original function that tries to round the output to more closely match chara motion timings
  31. float hookedGetFrameSpeed()
  32. {
  33. float frameSpeed = divaGetFrameSpeed();
  34. // below is somewhat based (in concept) on 140194ad0 (motion quantisation thingy func)
  35. // add the error compensation from last frame
  36. frameSpeed += FrameRateManager::fspeed_error;
  37. // separate whole and fractional parts of speed
  38. // float speed_rounded = floorf(frameSpeed);
  39. // float speed_remainder = frameSpeed - speed_rounded;
  40. float speed_rounded;
  41. float speed_remainder = modff(frameSpeed, &speed_rounded);
  42. // save the remainder as error compensation for next frame
  43. // use == 0 to detect new frame and vars must be written
  44. if (FrameRateManager::fspeed_error_next == 0)
  45. {
  46. FrameRateManager::fspeed_error_next = speed_remainder;
  47. FrameRateManager::fspeedhook_lastresult = speed_rounded;
  48. }
  49. return speed_rounded;
  50. }
  51. void FrameRateManager::Initialize(ComponentsManager*)
  52. {
  53. pvFrameRate = (float*)PV_FRAME_RATE_ADDRESS;
  54. frameSpeed = (float*)FRAME_SPEED_ADDRESS;
  55. aetFrameDuration = (float*)AET_FRAME_DURATION_ADDRESS;
  56. // The default is expected to be 1.0 / 60.0
  57. defaultAetFrameDuration = *aetFrameDuration;
  58. // This const variable is stored inside a data segment so we don't want to throw any access violations
  59. DWORD oldProtect;
  60. VirtualProtect((void*)AET_FRAME_DURATION_ADDRESS, sizeof(float), PAGE_EXECUTE_READWRITE, &oldProtect);
  61. // fix auto speed for high fps
  62. InjectCode((void*)0x140192d7b, { 0x90, 0x90, 0x90 });
  63. // fix frame speed slider initial value (should ignore effect of auto speed)
  64. InjectCode((void*)0x140338f2f, { 0xf3, 0x0f, 0x10, 0x0d, 0x61, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
  65. InjectCode((void*)0x140338f37, { 0x48, 0x8b, 0x8f, 0x80, 0x01, 0x00, 0x00 }); // MOV RCX, qword ptr [0x180 + RDI]
  66. InjectCode((void*)0x140338ebe, { 0xf3, 0x0f, 0x10, 0x0d, 0xd2, 0x18, 0xba, 0x00 }); // MOVSS XMM1, dword ptr [0x140eda798] (raw framespeed)
  67. InjectCode((void*)0x140338ec6, { 0x48, 0x8b, 0x05, 0xfb, 0xb1, 0xe5, 0x00 }); // MOV RAX, qword ptr [0x1411940c8]
  68. InjectCode((void*)0x140338ecd, { 0x48, 0x8b, 0x88, 0x80, 0x01, 0x00, 0x00 }); // MOV RCX, qword ptr [0x180 + RAX]
  69. // fix AETs
  70. InjectCode((void*)0x140170394, { 0xF3, 0x0F, 0x5E, 0x05, 0x34, 0xA3, 0xD6, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)
  71. // fix edit PV AETs (thanks lyb)
  72. InjectCode((void*)0x140192d30, { 0xF3, 0x0F, 0x10, 0x05, 0x5C, 0x02, 0x00, 0x00 }); // MOVSS XMM0, dword ptr [0x140192f94]
  73. // fix ageage hair effect
  74. InjectCode((void*)0x14054352f, { // MOV R9, &fspeedhook_lastresult
  75. 0x49,
  76. 0xB9,
  77. (uint8_t)((uint64_t)&fspeedhook_lastresult & 0xFF),
  78. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 8) & 0xFF),
  79. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 16) & 0xFF),
  80. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 24) & 0xFF),
  81. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 32) & 0xFF),
  82. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 40) & 0xFF),
  83. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 48) & 0xFF),
  84. (uint8_t)(((uint64_t)&fspeedhook_lastresult >> 56) & 0xFF),
  85. });
  86. InjectCode((void*)0x140543539, { 0xF3, 0x41, 0x0F, 0x59, 0x19 }); // MULSS XMM3, dword ptr [R9]
  87. InjectCode((void*)0x14054353e, { 0xEB, 0xB0 }); // JMP 0x1405434f0
  88. // fix wind effect
  89. InjectCode((void*)0x14053ca71, { 0xEB, 0x3F }); // JMP 0x14053cab2
  90. InjectCode((void*)0x14053cab2, { 0xF3, 0x0F, 0x10, 0x05, 0xFA, 0x53, 0x46, 0x00 }); // MOVSS XMM0, dword ptr [0x1409a1eb4] (60.0f)
  91. InjectCode((void*)0x14053caba, { 0xE9, 0x42, 0xFE, 0xFF, 0xFF }); // JMP 0x14053c901
  92. InjectCode((void*)0x14053c901, { 0xF3, 0x0F, 0x5E, 0x05, 0xC7, 0xDD, 0x99, 0x00 }); // DIVSS XMM0, dword ptr [0x140eda6d0] (framerate)
  93. InjectCode((void*)0x14053c909, { 0xE9, 0x68, 0x01, 0x00, 0x00 }); // JMP 0x14053ca76
  94. // replace divaGetFrameSpeed with a version that rounds the output to fix issues
  95. DetourTransactionBegin();
  96. DetourUpdateThread(GetCurrentThread());
  97. DetourAttach(&(PVOID&)divaGetFrameSpeed, hookedGetFrameSpeed);
  98. DetourTransactionCommit();
  99. }
  100. void FrameRateManager::Update()
  101. {
  102. // *aetFrameDuration = 1.0f / GetGameFrameRate();
  103. if (*(GameState*)CURRENT_GAME_STATE_ADDRESS == GS_GAME)
  104. {
  105. *pvFrameRate = 60.0f * motionSpeedMultiplier;
  106. }
  107. else
  108. {
  109. *pvFrameRate = 60.0f;
  110. }
  111. if (*(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_GAME_MAIN || *(SubGameState*)CURRENT_GAME_SUB_STATE_ADDRESS == SUB_DEMO)
  112. {
  113. // enable dynamic framerate
  114. *(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;
  115. // target framerate
  116. *(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = *pvFrameRate;
  117. // trying to fix meltdown's water
  118. //if ((uint64_t*)0x1411943f8 != nullptr)
  119. //*(float*)(*(uint64_t*)0x1411943f8 + 0x1c) = (60.0f / GetGameFrameRate()) / 15.0f;
  120. //*(float*)(*(uint64_t*)0x1411943f8 + 0x10) = *(float*)(*(uint64_t*)0x1411943f8);
  121. }
  122. else
  123. {
  124. // enable dynamic framerate
  125. *(bool*)USE_AUTO_FRAMESPEED_ADDRESS = true;
  126. // target framerate
  127. *(float*)AUTO_FRAMESPEED_TARGET_FRAMERATE_ADDRESS = 60.0f;
  128. }
  129. }
  130. void FrameRateManager::UpdateDraw2D()
  131. {
  132. // cycle the framespeed timing error once per frame
  133. fspeed_error = fspeed_error_next;
  134. fspeed_error_next = 0;
  135. }
  136. void FrameRateManager::InjectCode(void* address, const std::vector<uint8_t> data)
  137. {
  138. const size_t byteCount = data.size() * sizeof(uint8_t);
  139. DWORD oldProtect;
  140. VirtualProtect(address, byteCount, PAGE_EXECUTE_READWRITE, &oldProtect);
  141. memcpy(address, data.data(), byteCount);
  142. VirtualProtect(address, byteCount, oldProtect, nullptr);
  143. }
  144. }