Skip to content

Commit

Permalink
Add fps limit patch and movable window mode patch
Browse files Browse the repository at this point in the history
  • Loading branch information
esuo1198 committed Oct 13, 2024
1 parent 40ab237 commit f07574a
Show file tree
Hide file tree
Showing 12 changed files with 698 additions and 315 deletions.
9 changes: 6 additions & 3 deletions dist/config.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,6 @@ country_code = "JPN"

[patches]
version = "auto"
res = { x = 1920, y = 1080 }
windowed = false
vsync = false
unlock_songs = true

[patches.chn00]
Expand All @@ -19,6 +16,12 @@ unlock_songs = true
mode_collabo025 = false
mode_collabo026 = false

[graphics]
res = { x = 1920, y = 1080 }
windowed = false
vsync = false
fpslimit = 120

[audio]
wasapi_shared = true
asio = false
Expand Down
5 changes: 4 additions & 1 deletion meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,12 @@ library(
'src/helpers.cpp',
'src/poll.cpp',
'src/bnusio.cpp',
'src/patches/amauth.cpp',
'src/patches/dxgi.cpp',
'src/patches/fpslimiter.cpp',
'src/patches/audio.cpp',
'src/patches/qr.cpp',
'src/patches/amauth.cpp',
'src/patches/layeredfs.cpp',
'src/patches/versions/JPN00.cpp',
'src/patches/versions/JPN08.cpp',
'src/patches/versions/JPN39.cpp',
Expand Down
2 changes: 0 additions & 2 deletions src/constants.h
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
#pragma once
#include <xxhash.h>

#define crcPOLY 0x82f63b78

enum class GameVersion : XXH64_hash_t {
UNKNOWN = 0,
JPN00 = 0x4C07355966D815FB,
Expand Down
338 changes: 55 additions & 283 deletions src/dllmain.cpp

Large diffs are not rendered by default.

257 changes: 257 additions & 0 deletions src/patches/dxgi.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,257 @@
#define CINTERFACE
#define D3D11_NO_HELPERS
#define INITGUID
#pragma optimize("", off)

#include "d3d11.h"
#include "dxgi1_2.h"
#include "dxgi1_3.h"
#include "dxgi1_4.h"
#include "dxgi1_5.h"
#include "dxgi1_6.h"
#include "helpers.h"
#include <MinHook.h>
#pragma comment(lib, "d3d11.lib")
#include "d3d12.h"
#pragma comment(lib, "d3d12.lib")

#include "patches.h"
#include <intrin.h>

/*
* Reference: https://github.com/teknogods/OpenParrot/blob/master/OpenParrot/src/Functions/WindowedDxgi.cpp
*/

namespace patches::Dxgi {

// Local variables
static bool FpsLimiterEnable = false;
static bool DisableVSync = false;

// Prototypes
static HRESULT (STDMETHODCALLTYPE *g_oldSetFullscreenState) (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget);
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChain) (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain);
static HRESULT (STDMETHODCALLTYPE *g_oldSetFullscreenState1) (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget);
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChainForHwnd) (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd,
const DXGI_SWAP_CHAIN_DESC1 *pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc,
IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain);
static HRESULT (STDMETHODCALLTYPE *g_oldPresentWrap) (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);
static HRESULT (STDMETHODCALLTYPE *g_oldPresent1Wrap) (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags);
static HRESULT (STDMETHODCALLTYPE *g_oldCreateSwapChain2) (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain);
static HRESULT (WINAPI *g_origCreateDXGIFactory2) (UINT Flags, REFIID riid, void **ppFactory);
static HRESULT (WINAPI *g_origCreateDXGIFactory) (REFIID, void **);
static HRESULT (WINAPI *g_origD3D11CreateDeviceAndSwapChain) (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain,
ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel,
ID3D11DeviceContext **ppImmediateContext);

static HRESULT STDMETHODCALLTYPE SetFullscreenStateWrap (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget);
static HRESULT STDMETHODCALLTYPE CreateSwapChainWrap (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain);
static HRESULT STDMETHODCALLTYPE SetFullscreenState1Wrap (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget);
static HRESULT STDMETHODCALLTYPE CreateSwapChainForHwndWrap (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1 *pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, IDXGIOutput *pRestrictToOutput,
IDXGISwapChain1 **ppSwapChain);
static HRESULT STDMETHODCALLTYPE PresentWrap (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE Present1Wrap (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags);
static HRESULT STDMETHODCALLTYPE CreateSwapChain2Wrap (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc,
IDXGISwapChain **ppSwapChain);
static HRESULT WINAPI CreateDXGIFactory2Wrap (UINT Flags, REFIID riid, void **ppFactory);
static HRESULT WINAPI CreateDXGIFactoryWrap (REFIID riid, _COM_Outptr_ void **ppFactory);
static HRESULT WINAPI D3D11CreateDeviceAndSwapChainWrap (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain,
ID3D11Device **ppDevice, D3D_FEATURE_LEVEL *pFeatureLevel,
ID3D11DeviceContext **ppImmediateContext);

// Functions
template <typename T>
inline T
HookVtableFunction (T *functionPtr, T target) {
if (*functionPtr == target) return nullptr;

auto old = *functionPtr;
WRITE_MEMORY (functionPtr, T, target);
// injector::WriteMemory (functionPtr, target, true);

return old;
}

static HRESULT STDMETHODCALLTYPE
SetFullscreenStateWrap (IDXGISwapChain *This, BOOL Fullscreen, IDXGIOutput *pTarget) {
return S_OK;
}

static HRESULT STDMETHODCALLTYPE
CreateSwapChainWrap (IDXGIFactory *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) {
HRESULT hr = g_oldCreateSwapChain (This, pDevice, pDesc, ppSwapChain);

if (*ppSwapChain) {
if (FpsLimiterEnable || DisableVSync) {
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
}
}

return hr;
}

static HRESULT STDMETHODCALLTYPE
SetFullscreenState1Wrap (IDXGISwapChain1 *This, BOOL Fullscreen, IDXGIOutput *pTarget) {
return S_OK;
}

static HRESULT STDMETHODCALLTYPE
CreateSwapChainForHwndWrap (IDXGIFactory2 *This, IUnknown *pDevice, HWND hWnd, const DXGI_SWAP_CHAIN_DESC1 *pDesc,
const DXGI_SWAP_CHAIN_FULLSCREEN_DESC *pFullscreenDesc, IDXGIOutput *pRestrictToOutput, IDXGISwapChain1 **ppSwapChain) {
HRESULT hr = g_oldCreateSwapChainForHwnd (This, pDevice, hWnd, pDesc, NULL, pRestrictToOutput, ppSwapChain);

if (*ppSwapChain) {
if (FpsLimiterEnable || DisableVSync) {
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, Present1Wrap);
g_oldPresent1Wrap = (old2) ? old2 : g_oldPresent1Wrap;
}
}

return hr;
}

static HRESULT STDMETHODCALLTYPE
PresentWrap (IDXGISwapChain *pSwapChain, UINT SyncInterval, UINT Flags) {
if (DisableVSync) SyncInterval = 0;

if (FpsLimiterEnable) patches::FpsLimiter::Update ();

return g_oldPresentWrap (pSwapChain, SyncInterval, Flags);
}

static HRESULT STDMETHODCALLTYPE
Present1Wrap (IDXGISwapChain1 *pSwapChain, UINT SyncInterval, UINT Flags) {
if (DisableVSync) SyncInterval = 0;

if (FpsLimiterEnable) patches::FpsLimiter::Update ();

return g_oldPresent1Wrap (pSwapChain, SyncInterval, Flags);
}

static HRESULT STDMETHODCALLTYPE
CreateSwapChain2Wrap (IDXGIFactory2 *This, IUnknown *pDevice, DXGI_SWAP_CHAIN_DESC *pDesc, IDXGISwapChain **ppSwapChain) {
HRESULT hr = g_oldCreateSwapChain2 (This, pDevice, pDesc, ppSwapChain);

if (*ppSwapChain) {
if (FpsLimiterEnable || DisableVSync) {
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
}
}

return hr;
}

static HRESULT WINAPI
CreateDXGIFactory2Wrap (UINT Flags, REFIID riid, void **ppFactory) {
HRESULT hr = g_origCreateDXGIFactory2 (Flags, riid, ppFactory);

if (SUCCEEDED (hr)) {
int factoryType = 0;

if (IsEqualIID (riid, IID_IDXGIFactory1)) factoryType = 1;
else if (IsEqualIID (riid, IID_IDXGIFactory2)) factoryType = 2;
else if (IsEqualIID (riid, IID_IDXGIFactory3)) factoryType = 3;
else if (IsEqualIID (riid, IID_IDXGIFactory4)) factoryType = 4;
else if (IsEqualIID (riid, IID_IDXGIFactory5)) factoryType = 5;
else if (IsEqualIID (riid, IID_IDXGIFactory6)) factoryType = 6;
else if (IsEqualIID (riid, IID_IDXGIFactory7)) factoryType = 7;

IDXGIFactory2 *factory = (IDXGIFactory2 *)*ppFactory;

auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChain, CreateSwapChain2Wrap);
g_oldCreateSwapChain2 = (old) ? old : g_oldCreateSwapChain2;
}

return hr;
}

static HRESULT WINAPI
CreateDXGIFactoryWrap (REFIID riid, _COM_Outptr_ void **ppFactory) {
HRESULT hr = g_origCreateDXGIFactory (riid, ppFactory);

if (SUCCEEDED (hr)) {
int factoryType = 0;

if (IsEqualIID (riid, IID_IDXGIFactory1)) factoryType = 1;
else if (IsEqualIID (riid, IID_IDXGIFactory2)) factoryType = 2;
else if (IsEqualIID (riid, IID_IDXGIFactory3)) factoryType = 3;
else if (IsEqualIID (riid, IID_IDXGIFactory4)) factoryType = 4;
else if (IsEqualIID (riid, IID_IDXGIFactory5)) factoryType = 5;
else if (IsEqualIID (riid, IID_IDXGIFactory6)) factoryType = 6;
else if (IsEqualIID (riid, IID_IDXGIFactory7)) factoryType = 7;

if (factoryType >= 0) {
IDXGIFactory *factory = (IDXGIFactory *)*ppFactory;

auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChain, CreateSwapChainWrap);
g_oldCreateSwapChain = (old) ? old : g_oldCreateSwapChain;
}

if (factoryType >= 2) {
IDXGIFactory2 *factory = (IDXGIFactory2 *)*ppFactory;

auto old = HookVtableFunction (&factory->lpVtbl->CreateSwapChainForHwnd, CreateSwapChainForHwndWrap);
g_oldCreateSwapChainForHwnd = (old) ? old : g_oldCreateSwapChainForHwnd;
}
}

return hr;
}

static HRESULT WINAPI
D3D11CreateDeviceAndSwapChainWrap (IDXGIAdapter *pAdapter, D3D_DRIVER_TYPE DriverType, HMODULE Software, UINT Flags,
const D3D_FEATURE_LEVEL *pFeatureLevels, UINT FeatureLevels, UINT SDKVersion,
/*const*/ DXGI_SWAP_CHAIN_DESC *pSwapChainDesc, IDXGISwapChain **ppSwapChain, ID3D11Device **ppDevice,
D3D_FEATURE_LEVEL *pFeatureLevel, ID3D11DeviceContext **ppImmediateContext) {
HRESULT hr = g_origD3D11CreateDeviceAndSwapChain (pAdapter, DriverType, Software, Flags, pFeatureLevels, FeatureLevels, SDKVersion,
pSwapChainDesc, ppSwapChain, ppDevice, pFeatureLevel, ppImmediateContext);

if (ppSwapChain) {
if (FpsLimiterEnable) {
auto old2 = HookVtableFunction (&(*ppSwapChain)->lpVtbl->Present, PresentWrap);
g_oldPresentWrap = (old2) ? old2 : g_oldPresentWrap;
}
}

return hr;
}

void
Init () {
bool vsync = false;
i32 fpsLimit = 120;

auto configPath = std::filesystem::current_path () / "config.toml";
std::unique_ptr<toml_table_t, void (*) (toml_table_t *)> config_ptr (openConfig (configPath), toml_free);
if (config_ptr) {
auto graphics = openConfigSection (config_ptr.get (), "graphics");
if (graphics) {
vsync = readConfigBool (graphics, "vsync", vsync);
fpsLimit = readConfigInt (graphics, "fpslimit", fpsLimit);
}
}

DisableVSync = !vsync;
FpsLimiterEnable = fpsLimit > 0;
patches::FpsLimiter::Init ((float)fpsLimit);

MH_Initialize ();
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory", (LPVOID)CreateDXGIFactoryWrap, (void **)&g_origCreateDXGIFactory);
MH_CreateHookApi (L"dxgi.dll", "CreateDXGIFactory2", (LPVOID)CreateDXGIFactory2Wrap, (void **)&g_origCreateDXGIFactory2);
MH_CreateHookApi (L"d3d11.dll", "D3D11CreateDeviceAndSwapChain", (LPVOID)D3D11CreateDeviceAndSwapChainWrap,
(void **)&g_origD3D11CreateDeviceAndSwapChain);
MH_EnableHook (MH_ALL_HOOKS);
}

} // namespace patches::Dxgi
65 changes: 65 additions & 0 deletions src/patches/fpslimiter.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
#include "helpers.h"
#include "patches.h"

namespace patches::FpsLimiter {

/*
* Reference: https://github.com/teknogods/OpenParrot/blob/master/OpenParrot/src/Functions/FpsLimiter.cpp
*/

static LARGE_INTEGER PerformanceCount1;
static LARGE_INTEGER PerformanceCount2;
static bool bOnce1 = false;
static double targetFrameTime = 1000.0 / 60.0;
static double t = 0.0;
static u32 i = 0;

void
Init (float fpsLimit) {
targetFrameTime = 1000.0 / fpsLimit;
}

void
Update () {
if (!bOnce1) {
bOnce1 = true;
QueryPerformanceCounter (&PerformanceCount1);
PerformanceCount1.QuadPart = PerformanceCount1.QuadPart >> i;
}

while (true) {
QueryPerformanceCounter (&PerformanceCount2);

if (t == 0.0) {
LARGE_INTEGER PerformanceCount3;
static bool bOnce2 = false;

if (!bOnce2) {
bOnce2 = true;
QueryPerformanceFrequency (&PerformanceCount3);
i = 0;
t = 1000.0 / (double)PerformanceCount3.QuadPart;
auto v = t * 2147483648.0;
if (60000.0 > v) {
while (true) {
++i;
v *= 2.0;
t *= 2.0;
if (60000.0 <= v) break;
}
}
}
SleepEx (0, 1);
break;
}

if (((double)((PerformanceCount2.QuadPart >> i) - PerformanceCount1.QuadPart) * t) >= targetFrameTime) break;

SleepEx (0, 1);
}

QueryPerformanceCounter (&PerformanceCount2);
PerformanceCount1.QuadPart = PerformanceCount2.QuadPart >> i;
}

}
Loading

0 comments on commit f07574a

Please sign in to comment.