// DInput8Proxy.cpp -- dinput8.dll proxy loader
//
// Forwards all DirectInput8 exports to the real system DLL while
// allowing our DLL to load alongside the game via DLL search order.
#include "DInput8Proxy.h"

#pragma comment(linker, "/EXPORT:DirectInput8Create=_DirectInput8Create@20")
#pragma comment(linker, "/EXPORT:DllCanUnloadNow=_DllCanUnloadNow@0")
#pragma comment(linker, "/EXPORT:DllGetClassObject=_DllGetClassObject@12")
#pragma comment(linker, "/EXPORT:DllRegisterServer=_DllRegisterServer@0")
#pragma comment(linker, "/EXPORT:DllUnregisterServer=_DllUnregisterServer@0")
#pragma comment(linker, "/EXPORT:GetdfDIJoystick=_GetdfDIJoystick@0")

HMODULE g_realDinput8 = NULL;

static FARPROC g_pDirectInput8Create = NULL;
static FARPROC g_pDllCanUnloadNow = NULL;
static FARPROC g_pDllGetClassObject = NULL;
static FARPROC g_pDllRegisterServer = NULL;
static FARPROC g_pDllUnregisterServer = NULL;
static FARPROC g_pGetdfDIJoystick = NULL;

extern "C" HRESULT WINAPI DirectInput8Create(HINSTANCE hinst, DWORD dwVersion, REFIID riidltf, LPVOID* ppvOut, LPUNKNOWN punkOuter) {
    if (!g_pDirectInput8Create) return E_FAIL;
    typedef HRESULT(WINAPI* Fn)(HINSTANCE, DWORD, REFIID, LPVOID*, LPUNKNOWN);
    return ((Fn)g_pDirectInput8Create)(hinst, dwVersion, riidltf, ppvOut, punkOuter);
}
extern "C" HRESULT WINAPI DllCanUnloadNow(void) {
    if (!g_pDllCanUnloadNow) return S_OK;
    typedef HRESULT(WINAPI* Fn)(void);
    return ((Fn)g_pDllCanUnloadNow)();
}
extern "C" HRESULT WINAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) {
    if (!g_pDllGetClassObject) return E_FAIL;
    typedef HRESULT(WINAPI* Fn)(REFCLSID, REFIID, LPVOID*);
    return ((Fn)g_pDllGetClassObject)(rclsid, riid, ppv);
}
extern "C" HRESULT WINAPI DllRegisterServer(void) {
    if (!g_pDllRegisterServer) return E_FAIL;
    typedef HRESULT(WINAPI* Fn)(void);
    return ((Fn)g_pDllRegisterServer)();
}
extern "C" HRESULT WINAPI DllUnregisterServer(void) {
    if (!g_pDllUnregisterServer) return E_FAIL;
    typedef HRESULT(WINAPI* Fn)(void);
    return ((Fn)g_pDllUnregisterServer)();
}
extern "C" LPVOID WINAPI GetdfDIJoystick(void) {
    if (!g_pGetdfDIJoystick) return NULL;
    typedef LPVOID(WINAPI* Fn)(void);
    return ((Fn)g_pGetdfDIJoystick)();
}

void LoadRealDinput8()
{
    char ourPath[MAX_PATH] = { 0 };
    GetModuleFileNameA(GetModuleHandleA("dinput8.dll"), ourPath, MAX_PATH);

    char sysDir[MAX_PATH] = { 0 };
    GetSystemDirectoryA(sysDir, MAX_PATH);

    char path[MAX_PATH];
    snprintf(path, MAX_PATH, "%s\\dinput8.dll", sysDir);

    if (_stricmp(path, ourPath) == 0)
    {
        MessageBoxA(NULL, "Chaos Error: System dinput8.dll path matches our own DLL!", "Fatal Error", MB_OK | MB_ICONERROR);
        TerminateProcess(GetCurrentProcess(), 1);
        return;
    }

    g_realDinput8 = LoadLibraryA(path);

    if (!g_realDinput8) {
        MessageBoxA(NULL, "Chaos Error: Could not locate system dinput8.dll", "Fatal Error", MB_OK | MB_ICONERROR);
        TerminateProcess(GetCurrentProcess(), 1);
        return;
    }

    g_pDirectInput8Create = GetProcAddress(g_realDinput8, "DirectInput8Create");
    g_pDllCanUnloadNow = GetProcAddress(g_realDinput8, "DllCanUnloadNow");
    g_pDllGetClassObject = GetProcAddress(g_realDinput8, "DllGetClassObject");
    g_pDllRegisterServer = GetProcAddress(g_realDinput8, "DllRegisterServer");
    g_pDllUnregisterServer = GetProcAddress(g_realDinput8, "DllUnregisterServer");
    g_pGetdfDIJoystick = GetProcAddress(g_realDinput8, "GetdfDIJoystick");
}
