#include <Windows.h>
#include <filesystem>
namespace fs = std::filesystem;

static HMODULE real_version = nullptr;

void LoadRealVersionDLL()
{
    if (!real_version)
    {
        real_version = LoadLibraryW(L"C:\\Windows\\System32\\version.dll");
    }
}

extern "C" BOOL WINAPI GetFileVersionInfoA(LPCSTR file, DWORD handle, DWORD len, LPVOID data)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(LPCSTR, DWORD, DWORD, LPVOID);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoA");
    return real(file, handle, len, data);
}

extern "C" BOOL WINAPI GetFileVersionInfoW(LPCWSTR file, DWORD handle, DWORD len, LPVOID data)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(LPCWSTR, DWORD, DWORD, LPVOID);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoW");
    return real(file, handle, len, data);
}

extern "C" BOOL WINAPI GetFileVersionInfoExA(DWORD dwFlags, LPCSTR lpstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(DWORD, LPCSTR, DWORD, DWORD, LPVOID);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoExA");
    return real(dwFlags, lpstrFilename, dwHandle, dwLen, lpData);
}

extern "C" BOOL WINAPI GetFileVersionInfoExW(DWORD dwFlags, LPCWSTR lpwstrFilename, DWORD dwHandle, DWORD dwLen, LPVOID lpData)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(DWORD, LPCWSTR, DWORD, DWORD, LPVOID);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoExW");
    return real(dwFlags, lpwstrFilename, dwHandle, dwLen, lpData);
}

extern "C" DWORD WINAPI GetFileVersionInfoSizeA(LPCSTR lptstrFilename, LPDWORD lpdwHandle)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(LPCSTR, LPDWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeA");
    return real(lptstrFilename, lpdwHandle);
}

extern "C" DWORD WINAPI GetFileVersionInfoSizeW(LPCWSTR lptstrFilename, LPDWORD lpdwHandle)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(LPCWSTR, LPDWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeW");
    return real(lptstrFilename, lpdwHandle);
}

extern "C" DWORD WINAPI GetFileVersionInfoSizeExA(DWORD dwFlags, LPCSTR lpwstrFilename, LPDWORD lpdwHandle)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(DWORD, LPCSTR, LPDWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeExA");
    return real(dwFlags, lpwstrFilename, lpdwHandle);
}

extern "C" DWORD WINAPI GetFileVersionInfoSizeExW(DWORD dwFlags, LPCWSTR lpwstrFilename, LPDWORD lpdwHandle)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(DWORD, LPCWSTR, LPDWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "GetFileVersionInfoSizeExW");
    return real(dwFlags, lpwstrFilename, lpdwHandle);
}

extern "C" DWORD WINAPI VerFindFileA(DWORD uFlags, LPCSTR szFileName, LPCSTR szWinDir, LPCSTR szAppDir, LPSTR szCurDir, UINT* pcchCurDir, LPSTR szDestDir, UINT* pcchDestDir)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(DWORD, LPCSTR, LPCSTR, LPCSTR, LPSTR, UINT*, LPSTR, UINT*);
    static Fn real = (Fn)GetProcAddress(real_version, "VerFindFileA");
    return real(uFlags, szFileName, szWinDir, szAppDir, szCurDir, pcchCurDir, szDestDir, pcchDestDir);
}

extern "C" DWORD WINAPI VerFindFileW(DWORD uFlags, LPCWSTR szFileName, LPCWSTR szWinDir, LPCWSTR szAppDir, LPWSTR szCurDir, UINT* pcchCurDir, LPWSTR szDestDir, UINT* pcchDestDir)
{
    LoadRealVersionDLL();
    using Fn = DWORD(WINAPI*)(DWORD, LPCWSTR, LPCWSTR, LPCWSTR, LPWSTR, UINT*, LPWSTR, UINT*);
    static Fn real = (Fn)GetProcAddress(real_version, "VerFindFileW");
    return real(uFlags, szFileName, szWinDir, szAppDir, szCurDir, pcchCurDir, szDestDir, pcchDestDir);
}

extern "C" DWORD WINAPI VerLanguageNameA(DWORD wLang, LPSTR szLang, DWORD cchLang)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(DWORD, LPSTR, DWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "VerLanguageNameA");
    return real(wLang, szLang, cchLang);
}

extern "C" DWORD WINAPI VerLanguageNameW(DWORD wLang, LPWSTR szLang, DWORD cchLang)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(DWORD, LPWSTR, DWORD);
    static Fn real = (Fn)GetProcAddress(real_version, "VerLanguageNameW");
    return real(wLang, szLang, cchLang);
}

extern "C" BOOL WINAPI VerQueryValueA(LPCVOID pBlock, LPCSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(LPCVOID, LPCSTR, LPVOID*, PUINT);
    static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueA");
    return real(pBlock, lpSubBlock, lplpBuffer, puLen);
}

extern "C" BOOL WINAPI VerQueryValueW(LPCVOID pBlock, LPCWSTR lpSubBlock, LPVOID* lplpBuffer, PUINT puLen)
{
    LoadRealVersionDLL();
    using Fn = BOOL(WINAPI*)(LPCVOID, LPCWSTR, LPVOID*, PUINT);
    static Fn real = (Fn)GetProcAddress(real_version, "VerQueryValueW");
    return real(pBlock, lpSubBlock, lplpBuffer, puLen);
}

// Charge les ASI dans le répertoire courant
void LoadAllASI()
{
    auto path = fs::current_path();
    for (const auto& entry : fs::directory_iterator(path))
    {
        if (entry.path().extension() == ".asi")
        {
            LoadLibraryW(entry.path().c_str());
        }
    }
}

BOOL APIENTRY DllMain(HMODULE hModule, DWORD reason, LPVOID)
{
    if (reason == DLL_PROCESS_ATTACH)
    {
        LoadAllASI();
    }
    return TRUE;
}
