From a693fcab267468e8506448a492c756bdf070ba5b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Michael=20M=C3=BCller?= <michael@fds-team.de>
Date: Mon, 5 Jan 2015 18:11:53 +0100
Subject: nvapi: First implementation.

---
 configure.ac                 |   9 +
 dlls/nvapi/Makefile.in       |   4 +
 dlls/nvapi/nvapi.c           | 462 +++++++++++++++++++++++++++++++++++++++++++
 dlls/nvapi/nvapi.spec        |   5 +
 dlls/nvapi/tests/Makefile.in |   5 +
 dlls/nvapi/tests/nvapi.c     | 276 ++++++++++++++++++++++++++
 dlls/nvapi64/Makefile.in     |   5 +
 dlls/nvapi64/nvapi64.spec    |   5 +
 include/Makefile.in          |   1 +
 include/nvapi.h              |  76 +++++++
 10 files changed, 848 insertions(+)
 create mode 100644 dlls/nvapi/Makefile.in
 create mode 100644 dlls/nvapi/nvapi.c
 create mode 100644 dlls/nvapi/nvapi.spec
 create mode 100644 dlls/nvapi/tests/Makefile.in
 create mode 100644 dlls/nvapi/tests/nvapi.c
 create mode 100644 dlls/nvapi64/Makefile.in
 create mode 100644 dlls/nvapi64/nvapi64.spec
 create mode 100644 include/nvapi.h

diff --git a/configure.ac b/configure.ac
index 8a98996..efe32af 100644
--- a/configure.ac
+++ b/configure.ac
@@ -190,6 +190,12 @@ esac
 dnl enable_win16 defaults to yes on x86, to no on other CPUs
 enable_win16=${enable_win16:-no}
 enable_win64=${enable_win64:-no}
+if test "x$enable_win64" != "xyes"
+then
+  enable_win32="yes"
+else
+  enable_win32="no"
+fi
 
 dnl Disable winetest too if tests are disabled
 enable_winetest=${enable_winetest:-$enable_tests}
@@ -3159,4 +3165,7 @@ WINE_CONFIG_DLL(ntoskrnl.exe,,[implib])
 WINE_CONFIG_DLL(ntprint)
 WINE_CONFIG_TEST(dlls/ntprint/tests)
+WINE_CONFIG_DLL(nvapi,enable_win32)
+WINE_CONFIG_TEST(dlls/nvapi/tests)
+WINE_CONFIG_DLL(nvapi64,enable_win64)
 WINE_CONFIG_DLL(nvcuda)
 WINE_CONFIG_TEST(dlls/nvcuda/tests)
diff --git a/dlls/nvapi/Makefile.in b/dlls/nvapi/Makefile.in
new file mode 100644
index 0000000..606177f
--- /dev/null
+++ b/dlls/nvapi/Makefile.in
@@ -0,0 +1,4 @@
+MODULE    = nvapi.dll
+
+C_SRCS = \
+	nvapi.c
diff --git a/dlls/nvapi/nvapi.c b/dlls/nvapi/nvapi.c
new file mode 100644
index 0000000..2c7eceb
--- /dev/null
+++ b/dlls/nvapi/nvapi.c
@@ -0,0 +1,462 @@
+/*
+ * Copyright (C) 2015 Michael Müller
+ * Copyright (C) 2015 Sebastian Lackner
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include "config.h"
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "winternl.h"
+#include "wine/debug.h"
+#include "wine/list.h"
+#include "nvapi.h"
+#include "d3d9.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(nvapi);
+
+#define FAKE_PHYSICAL_GPU ((NvPhysicalGpuHandle)0xdead0001)
+#define FAKE_DISPLAY ((NvDisplayHandle)0xdead0002)
+
+#if defined(__i386__) || defined(__x86_64__)
+
+static NvAPI_Status CDECL unimplemented_stub(unsigned int offset)
+{
+    FIXME("function 0x%x is unimplemented!\n", offset);
+    return NVAPI_ERROR;
+}
+
+#ifdef __i386__
+
+#include "pshpack1.h"
+struct thunk
+{
+    unsigned char  push_ebp;
+    unsigned short mov_esp_ebp;
+    unsigned char  sub_0x08_esp[3];
+    unsigned char  mov_dword_esp[3];
+    unsigned int   offset;
+    unsigned char  mov_eax;
+    void           *stub;
+    unsigned short call_eax;
+    unsigned char  leave;
+    unsigned char  ret;
+};
+#include "poppack.h"
+
+static void* prepare_thunk(struct thunk *thunk, unsigned int offset)
+{
+    thunk->push_ebp         = 0x55;
+    thunk->mov_esp_ebp      = 0xE589;
+    thunk->sub_0x08_esp[0]  = 0x83;
+    thunk->sub_0x08_esp[1]  = 0xEC;
+    thunk->sub_0x08_esp[2]  = 0x08;
+    thunk->mov_dword_esp[0] = 0xC7;
+    thunk->mov_dword_esp[1] = 0x04;
+    thunk->mov_dword_esp[2] = 0x24;
+    thunk->offset           = offset;
+    thunk->mov_eax          = 0xB8;
+    thunk->stub             = &unimplemented_stub;
+    thunk->call_eax         = 0xD0FF;
+    thunk->leave            = 0xC9;
+    thunk->ret              = 0xC3;
+    return thunk;
+}
+
+#else
+
+#include "pshpack1.h"
+struct thunk
+{
+    unsigned short mov_rcx;
+    unsigned int   offset;
+    unsigned int   zero;
+    unsigned short mov_rax;
+    void           *stub;
+    unsigned short jmp_rax;
+};
+#include "poppack.h"
+
+static void* prepare_thunk(struct thunk *thunk, unsigned int offset)
+{
+    thunk->mov_rcx           = 0xB948;
+    thunk->offset            = offset;
+    thunk->zero              = 0;
+    thunk->mov_rax           = 0xB848;
+    thunk->stub              = &unimplemented_stub;
+    thunk->jmp_rax           = 0xE0FF;
+    return thunk;
+}
+
+#endif
+
+struct thunk_entry
+{
+    struct list entry;
+    int num_thunks;
+    struct thunk thunks[0];
+};
+
+static struct list unimplemented_thunks = LIST_INIT( unimplemented_thunks );
+static SYSTEM_BASIC_INFORMATION system_info;
+
+static RTL_CRITICAL_SECTION unimplemented_thunk_section;
+static RTL_CRITICAL_SECTION_DEBUG critsect_debug =
+{
+    0, 0, &unimplemented_thunk_section,
+    { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
+      0, 0, { (DWORD_PTR)(__FILE__ ": unimplemented_thunk_section") }
+};
+static RTL_CRITICAL_SECTION unimplemented_thunk_section = { &critsect_debug, -1, 0, 0, 0, 0 };
+
+static void* lookup_thunk_function(unsigned int offset)
+{
+    struct list *ptr;
+    unsigned int i;
+
+    /* check for existing thunk */
+    LIST_FOR_EACH( ptr, &unimplemented_thunks )
+    {
+        struct thunk_entry *entry = LIST_ENTRY( ptr, struct thunk_entry, entry );
+        for (i = 0; i < entry->num_thunks; i++)
+            if (entry->thunks[i].offset == offset)
+                return &entry->thunks[i];
+    }
+
+    return NULL;
+}
+
+static void* allocate_thunk_function(unsigned int offset)
+{
+    struct thunk_entry *entry;
+    struct list *ptr;
+
+    /* append after last existing thunk if possible */
+    if ((ptr = list_tail( &unimplemented_thunks )))
+    {
+        entry = LIST_ENTRY( ptr, struct thunk_entry, entry );
+        if (FIELD_OFFSET( struct thunk_entry, thunks[entry->num_thunks + 1] ) <= system_info.PageSize)
+            return prepare_thunk( &entry->thunks[entry->num_thunks++], offset );
+    }
+
+    /* allocate a new block */
+    entry = VirtualAlloc( NULL, system_info.PageSize, MEM_COMMIT | MEM_RESERVE,
+                          PAGE_EXECUTE_READWRITE );
+    if (entry)
+    {
+        list_add_tail( &unimplemented_thunks, &entry->entry );
+        entry->num_thunks = 1;
+        return prepare_thunk( &entry->thunks[0], offset );
+    }
+
+    return NULL;
+}
+
+static void* get_thunk_function(unsigned int offset)
+{
+    void *ret;
+    TRACE("(%x)\n", offset);
+
+    EnterCriticalSection( &unimplemented_thunk_section );
+    ret = lookup_thunk_function( offset );
+    if (!ret) ret = allocate_thunk_function( offset );
+    LeaveCriticalSection( &unimplemented_thunk_section );
+    return ret;
+}
+
+static void init_thunks(void)
+{
+    NtQuerySystemInformation( SystemBasicInformation, &system_info, sizeof(system_info), NULL );
+    /* we assume here that system_info.PageSize will always be great enough to hold at least one thunk */
+}
+
+static void free_thunks(void)
+{
+    struct list *ptr, *ptr2;
+    EnterCriticalSection( &unimplemented_thunk_section );
+    LIST_FOR_EACH_SAFE( ptr, ptr2, &unimplemented_thunks )
+    {
+        struct thunk_entry *entry = LIST_ENTRY( ptr, struct thunk_entry, entry );
+        list_remove( ptr );
+        VirtualFree( entry, 0, MEM_RELEASE );
+    }
+    LeaveCriticalSection( &unimplemented_thunk_section );
+}
+
+#else
+
+static NvAPI_Status CDECL unimplemented_stub()
+{
+    FIXME("function is unimplemented!\n");
+    return NVAPI_ERROR;
+}
+
+static void* get_thunk_function(unsigned int offset)
+{
+    TRACE("(%x)\n", offset);
+    return &unimplemented_stub;
+}
+
+static void init_thunks(void)
+{
+    /* unimplemented */
+}
+
+static void free_thunks(void)
+{
+    /* unimplemented */
+}
+
+#endif
+
+
+static NvAPI_Status CDECL NvAPI_Initialize(void)
+{
+    TRACE("()\n");
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Unknown1(NV_UNKNOWN_1 *param)
+{
+    TRACE("(%p)\n", param);
+
+    if (!param)
+        return NVAPI_INVALID_ARGUMENT;
+
+    if (param->version != NV_UNKNOWN_1_VER)
+        return NVAPI_INCOMPATIBLE_STRUCT_VERSION;
+
+    param->gpu_count = 1;
+    param->gpus[0].gpuHandle = FAKE_PHYSICAL_GPU;
+    param->gpus[0].unknown2 = 11;
+
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Unknown2(NvPhysicalGpuHandle gpuHandle, NvPhysicalGpuHandle *retHandle)
+{
+    TRACE("(%p, %p)\n", gpuHandle, retHandle);
+
+    if (!gpuHandle)
+        return NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE;
+
+    if (!retHandle)
+        return NVAPI_INVALID_ARGUMENT;
+
+    if (gpuHandle == FAKE_PHYSICAL_GPU)
+        *retHandle = (void *)gpuHandle;
+    else
+    {
+        FIXME("invalid handle: %p\n", gpuHandle);
+        *retHandle = (void*)0xffffffff;
+    }
+
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Unknown3(NvPhysicalGpuHandle gpuHandle, NvPhysicalGpuHandle *retHandle)
+{
+    TRACE("(%p, %p)\n", gpuHandle, retHandle);
+
+    if (!gpuHandle || !retHandle)
+        return NVAPI_INVALID_ARGUMENT;
+
+    if (gpuHandle == FAKE_PHYSICAL_GPU)
+        *retHandle = (void *)gpuHandle;
+    else
+    {
+        FIXME("invalid handle: %p\n", gpuHandle);
+        *retHandle = (void *)0xffffffff;
+    }
+
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_GetDisplayDriverVersion(NvDisplayHandle hNvDisplay, NV_DISPLAY_DRIVER_VERSION *pVersion)
+{
+    NvAPI_ShortString build_str = {'r','3','3','7','_','0','0','-','1','8','9',0};
+    NvAPI_ShortString adapter = {'G','e','F','o','r','c','e',' ','9','9','9',' ','G','T','X', 0};
+    /* TODO: find a good way to get the graphic card name, EnumDisplayDevices is useless in Wine */
+    /* For now we return the non existing GeForce 999 GTX as graphic card name */
+
+    TRACE("(%p, %p)\n", hNvDisplay, pVersion);
+
+    if (hNvDisplay && hNvDisplay != FAKE_DISPLAY)
+    {
+        FIXME("invalid display handle: %p\n", hNvDisplay);
+        return NVAPI_INVALID_HANDLE;
+    }
+
+    if (!pVersion)
+        return NVAPI_INVALID_ARGUMENT;
+
+    pVersion->drvVersion = 33788;
+    pVersion->bldChangeListNum = 0;
+    memcpy(pVersion->szBuildBranchString, build_str, sizeof(build_str));
+    memcpy(pVersion->szAdapterString, adapter, sizeof(adapter));
+
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_GetAssociatedNvidiaDisplayHandle(const char *szDisplayName, NvDisplayHandle *pNvDispHandle)
+{
+    TRACE("(%s, %p)\n", szDisplayName, pNvDispHandle);
+
+    *pNvDispHandle = FAKE_DISPLAY;
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_GetPhysicalGPUsFromDisplay(NvDisplayHandle hNvDisp, NvPhysicalGpuHandle nvGPUHandle[NVAPI_MAX_PHYSICAL_GPUS], NvU32 *pGpuCount)
+{
+    TRACE("(%p, %p, %p)\n", hNvDisp, nvGPUHandle, pGpuCount);
+
+    nvGPUHandle[0] = FAKE_PHYSICAL_GPU;
+    *pGpuCount = 1;
+
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_Disable(void)
+{
+    TRACE("()\n");
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_IsEnabled(NvU8 *pIsStereoEnabled)
+{
+    TRACE("(%p)\n", pIsStereoEnabled);
+
+    *pIsStereoEnabled = 0;
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_CreateHandleFromIUnknown(void *pDevice, StereoHandle *pStereoHandle)
+{
+    TRACE("(%p, %p)\n", pDevice, pStereoHandle);
+    return NVAPI_STEREO_NOT_INITIALIZED;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_DestroyHandle(StereoHandle stereoHandle)
+{
+    TRACE("(%p)\n", stereoHandle);
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_Activate(StereoHandle stereoHandle)
+{
+    TRACE("(%p)\n", stereoHandle);
+    return NVAPI_STEREO_NOT_INITIALIZED;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_Deactivate(StereoHandle stereoHandle)
+{
+    TRACE("(%p)\n", stereoHandle);
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_IsActivated(StereoHandle stereoHandle, NvU8 *pIsStereoOn)
+{
+    TRACE("(%p, %p)\n", stereoHandle, pIsStereoOn);
+
+    *pIsStereoOn = 0;
+    return NVAPI_OK;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_GetSeparation(StereoHandle stereoHandle, float *pSeparationPercentage)
+{
+    TRACE("(%p, %p)\n", stereoHandle, pSeparationPercentage);
+    return NVAPI_STEREO_NOT_INITIALIZED;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_SetSeparation(StereoHandle stereoHandle, float newSeparationPercentage)
+{
+    TRACE("(%p, %f)\n", stereoHandle, newSeparationPercentage);
+    return NVAPI_STEREO_NOT_INITIALIZED;
+}
+
+static NvAPI_Status CDECL NvAPI_Stereo_Enable(void)
+{
+    TRACE("()\n");
+    return NVAPI_STEREO_NOT_INITIALIZED;
+}
+
+static NvAPI_Status CDECL NvAPI_D3D9_StretchRectEx(IDirect3DDevice9 *pDevice, IDirect3DResource9 *pSourceResource,
+                                                   const RECT *pSourceRect, IDirect3DResource9 *pDestResource,
+                                                   const RECT *pDestRect, D3DTEXTUREFILTERTYPE Filter)
+{
+    TRACE("(%p, %p, %p, %p, %p, %d)\n", pDevice, pSourceResource, pSourceRect, pDestResource, pDestRect, Filter);
+    return NVAPI_UNREGISTERED_RESOURCE;
+}
+
+void* CDECL nvapi_QueryInterface(unsigned int offset)
+{
+    static const struct
+    {
+        unsigned int offset;
+        void *function;
+    }
+    function_list[] =
+    {
+        {0x0150E828, NvAPI_Initialize},
+        {0xF951A4D1, NvAPI_GetDisplayDriverVersion},
+        {0x5786cc6e, NvAPI_Unknown1},
+        {0x6533ea3e, NvAPI_Unknown2},
+        {0x5380ad1a, NvAPI_Unknown3},
+        {0x35c29134, NvAPI_GetAssociatedNvidiaDisplayHandle},
+        {0x34ef9506, NvAPI_GetPhysicalGPUsFromDisplay},
+        {0x2ec50c2b, NvAPI_Stereo_Disable},
+        {0x348ff8e1, NvAPI_Stereo_IsEnabled},
+        {0xac7e37f4, NvAPI_Stereo_CreateHandleFromIUnknown},
+        {0x3a153134, NvAPI_Stereo_DestroyHandle},
+        {0xf6a1ad68, NvAPI_Stereo_Activate},
+        {0x2d68de96, NvAPI_Stereo_Deactivate},
+        {0x1fb0bc30, NvAPI_Stereo_IsActivated},
+        {0x451f2134, NvAPI_Stereo_GetSeparation},
+        {0x5c069fa3, NvAPI_Stereo_SetSeparation},
+        {0x239c4545, NvAPI_Stereo_Enable},
+        {0xaeaecd41, NvAPI_D3D9_StretchRectEx}
+    };
+    unsigned int i;
+    TRACE("(%x)\n", offset);
+
+    for (i = 0; i < sizeof(function_list) / sizeof(function_list[0]); i++)
+    {
+        if (function_list[i].offset == offset)
+            return function_list[i].function;
+    }
+
+    return get_thunk_function(offset);
+}
+
+BOOL WINAPI DllMain(HINSTANCE instance, DWORD reason, LPVOID reserved)
+{
+    TRACE("(%p, %u, %p)\n", instance, reason, reserved);
+    switch (reason)
+    {
+        case DLL_PROCESS_ATTACH:
+            DisableThreadLibraryCalls(instance);
+            init_thunks();
+            break;
+        case DLL_PROCESS_DETACH:
+            free_thunks();
+            break;
+    }
+
+    return TRUE;
+}
diff --git a/dlls/nvapi/nvapi.spec b/dlls/nvapi/nvapi.spec
new file mode 100644
index 0000000..f0c054b
--- /dev/null
+++ b/dlls/nvapi/nvapi.spec
@@ -0,0 +1,5 @@
+@ cdecl nvapi_QueryInterface(long)
+@ stub DllCanUnloadNow
+@ stub DllGetClassObject
+@ stub DllRegisterServer
+@ stub DllUnregisterServer
diff --git a/dlls/nvapi/tests/Makefile.in b/dlls/nvapi/tests/Makefile.in
new file mode 100644
index 0000000..a94df01
--- /dev/null
+++ b/dlls/nvapi/tests/Makefile.in
@@ -0,0 +1,5 @@
+TESTDLL   = nvapi.dll
+IMPORTS   = user32 shlwapi
+
+C_SRCS = \
+	nvapi.c
diff --git a/dlls/nvapi/tests/nvapi.c b/dlls/nvapi/tests/nvapi.c
new file mode 100644
index 0000000..d760a18
--- /dev/null
+++ b/dlls/nvapi/tests/nvapi.c
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2015 Michael Müller
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#include <stdarg.h>
+
+#include "windef.h"
+#include "winbase.h"
+#include "wingdi.h"
+#include "winuser.h"
+#include "shlwapi.h"
+#include "winerror.h"
+#include "nvapi.h"
+
+#include "wine/test.h"
+
+#define NvAPI_Initialize_Offset 0x0150E828
+#define NvAPI_GetDisplayDriverVersion_Offset 0xF951A4D1
+#define NvAPI_unknown1_Offset 0x5786cc6e
+#define NvAPI_unknown2_Offset 0x6533ea3e
+#define NvAPI_unknown3_Offset 0x5380ad1a
+
+static void* (CDECL *pnvapi_QueryInterface)(unsigned int offset);
+static NvAPI_Status (CDECL *pNvAPI_Initialize)(void);
+static NvAPI_Status (CDECL *pNvAPI_GetDisplayDriverVersion)(NvDisplayHandle hNvDisplay, NV_DISPLAY_DRIVER_VERSION *pVersion);
+static NvAPI_Status (CDECL *pNvAPI_unknown1)(void* param0);
+static NvAPI_Status (CDECL *pNvAPI_unknown2)(NvPhysicalGpuHandle gpuHandle, void *param1);
+static NvAPI_Status (CDECL *pNvAPI_unknown3)(void *param0, void *param1);
+
+static BOOL init(void)
+{
+    #ifdef __x86_64__
+    HMODULE nvapi = LoadLibraryA("nvapi64.dll");
+    #else
+    HMODULE nvapi = LoadLibraryA("nvapi.dll");
+    #endif
+
+    if (!nvapi)
+    {
+        skip("Could not load nvapi.dll\n");
+        return FALSE;
+    }
+
+    pnvapi_QueryInterface = (void *)GetProcAddress(nvapi, "nvapi_QueryInterface");
+    if (!pnvapi_QueryInterface)
+    {
+        win_skip("Failed to get entry point for nvapi_QueryInterface.\n");
+        return FALSE;
+    }
+
+    pNvAPI_Initialize = pnvapi_QueryInterface(NvAPI_Initialize_Offset);
+    pNvAPI_GetDisplayDriverVersion = pnvapi_QueryInterface(NvAPI_GetDisplayDriverVersion_Offset);
+    pNvAPI_unknown1 = pnvapi_QueryInterface(NvAPI_unknown1_Offset);
+    pNvAPI_unknown2 = pnvapi_QueryInterface(NvAPI_unknown2_Offset);
+    pNvAPI_unknown3 = pnvapi_QueryInterface(NvAPI_unknown3_Offset);
+
+    if (!pNvAPI_Initialize)
+    {
+        win_skip("Failed to get entry point for NvAPI_Initialize.\n");
+        return FALSE;
+    }
+
+    if (pNvAPI_Initialize())
+    {
+        skip("Failed to initialize nvapi.\n");
+        return FALSE;
+    }
+
+    return TRUE;
+}
+
+static void test_GetDisplayDriverVersion(void)
+{
+    NV_DISPLAY_DRIVER_VERSION version;
+    NvAPI_Status status;
+    DISPLAY_DEVICEA dinfo;
+    char *gpuName;
+
+    if (!pNvAPI_GetDisplayDriverVersion)
+    {
+        win_skip("NvAPI_GetDisplayDriverVersion export not found.\n");
+        return;
+    }
+
+    status = pNvAPI_GetDisplayDriverVersion(0, NULL);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    memset(&version, 0, sizeof(version));
+    version.version = NV_DISPLAY_DRIVER_VERSION_VER;
+
+    status = pNvAPI_GetDisplayDriverVersion((void *)0xdeadbeef, &version);
+    ok(status == NVAPI_INVALID_HANDLE, "Expected status NVAPI_INVALID_HANDLE, got %d\n", status);
+
+    memset(&dinfo, 0, sizeof(dinfo));
+    dinfo.cb = sizeof(dinfo);
+    ok(EnumDisplayDevicesA(NULL, 0, &dinfo, 0), "Failed to get monitor info\n");
+
+    trace("Device name: %s\n", (char*)&dinfo.DeviceName);
+    trace("Device string: %s\n", (char*)&dinfo.DeviceString);
+
+    gpuName = (char*)&dinfo.DeviceString;
+    StrTrimA(gpuName, " \t\r\n");
+
+    if (!strncmp(gpuName, "NVIDIA ", 7))
+        gpuName += 7;
+
+    memset(&version, 0, sizeof(version));
+    version.version = NV_DISPLAY_DRIVER_VERSION_VER;
+
+    status = pNvAPI_GetDisplayDriverVersion(0, &version);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+
+    trace("Driver version: %u\n", (unsigned int)version.drvVersion);
+    trace("Change list num: %u\n", (unsigned int)version.bldChangeListNum);
+    trace("Build string: %s\n", version.szBuildBranchString);
+    trace("Adapter string: %s\n", version.szAdapterString);
+
+    todo_wine ok(!strcmp(version.szAdapterString, gpuName), "Expected device name %s, got %s\n",
+                 gpuName, version.szAdapterString);
+}
+
+static void test_unknown1(void)
+{
+    NV_UNKNOWN_1 test;
+    NvAPI_Status status;
+    int i;
+
+    if (!pNvAPI_unknown1)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    status = pNvAPI_unknown1(NULL);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    memset(&test, 0, sizeof(test));
+    test.version = NV_UNKNOWN_1_VER;
+    status = pNvAPI_unknown1(&test);
+
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+    ok(test.gpu_count > 0, "Expected gpu_count > 0, got %d\n", test.gpu_count);
+
+    for (i = 0; i < test.gpu_count; i++)
+    {
+        ok(test.gpus[i].gpuHandle != NULL, "Expected gpus[%d].gpuHandle != 0, got %p\n", i, test.gpus[i].gpuHandle);
+        ok(test.gpus[i].unknown2 != 0, "Expected gpus[%d].unknown2 != 0, got %d\n", i, test.gpus[i].unknown2);
+    }
+
+    for (; i < sizeof(test.gpus) / sizeof(test.gpus[0]); i++)
+    {
+        ok(test.gpus[i].gpuHandle == NULL, "Expected gpus[%d].gpuHandle == NULL, got %p\n", i, test.gpus[i].gpuHandle);
+        ok(test.gpus[i].unknown2 == 0, "Expected gpus[%d].unknown2 == 0, got %d\n", i, test.gpus[i].unknown2);
+    }
+}
+
+static void test_unknown2(void)
+{
+    NV_UNKNOWN_1 test;
+    NvAPI_Status status;
+    NvPhysicalGpuHandle test2 = NULL;
+
+    if (!pNvAPI_unknown1)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    if (!pNvAPI_unknown2)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    status = pNvAPI_unknown2(NULL, NULL);
+    ok(status == NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE, "Expected status NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE, got %d\n", status);
+
+    memset(&test, 0, sizeof(test));
+    test.version = NV_UNKNOWN_1_VER;
+    status = pNvAPI_unknown1(&test);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+
+    status = pNvAPI_unknown2(test.gpus[0].gpuHandle, NULL);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    status = pNvAPI_unknown2(NULL, &test2);
+    ok(status == NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE, "Expected status NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE, got %d\n", status);
+
+    test2 = NULL;
+    status = pNvAPI_unknown2((void *)0xdeadbeef, &test2);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+    ok(test2 == (void*)0xffffffff, "Expected handle 0xffffffff, got %p\n", test2);
+
+    test2 = NULL;
+    status = pNvAPI_unknown2(test.gpus[0].gpuHandle, &test2);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+    ok(test2 == test.gpus[0].gpuHandle, "Expected handle %p, got %p\n", test.gpus[0].gpuHandle, test2);
+}
+
+static void test_unknown3(void)
+{
+    NV_UNKNOWN_1 test;
+    NvAPI_Status status;
+    NvPhysicalGpuHandle test2 = NULL;
+    NvPhysicalGpuHandle test3 = NULL;
+
+    if (!pNvAPI_unknown1)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    if (!pNvAPI_unknown2)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    if (!pNvAPI_unknown3)
+    {
+        win_skip("pNvAPI_unknown1 export not found.\n");
+        return;
+    }
+
+    status = pNvAPI_unknown3(NULL, NULL);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    memset(&test, 0, sizeof(test));
+    test.version = NV_UNKNOWN_1_VER;
+    status = pNvAPI_unknown1(&test);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+
+    status = pNvAPI_unknown2(test.gpus[0].gpuHandle, &test2);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+
+    status = pNvAPI_unknown3(test2, NULL);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    status = pNvAPI_unknown3(NULL, &test3);
+    ok(status == NVAPI_INVALID_ARGUMENT, "Expected status NVAPI_INVALID_ARGUMENT, got %d\n", status);
+
+    test3 = NULL;
+    status = pNvAPI_unknown3((void *)0xdeadbeef, &test3);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+    ok(test3 == (void*)0xffffffff, "Expected handle 0xffffffff, got %p\n", test3);
+
+    test3 = NULL;
+    status = pNvAPI_unknown3(test2, &test3);
+    ok(!status, "Expected status NVAPI_OK, got %d\n", status);
+    ok(test2 == test3, "Expected handle %p, got %p\n", test2, test3);
+}
+
+START_TEST( nvapi )
+{
+    if (!init())
+        return;
+
+    test_GetDisplayDriverVersion();
+    test_unknown1();
+    test_unknown2();
+    test_unknown3();
+}
diff --git a/dlls/nvapi64/Makefile.in b/dlls/nvapi64/Makefile.in
new file mode 100644
index 0000000..80e2d6b
--- /dev/null
+++ b/dlls/nvapi64/Makefile.in
@@ -0,0 +1,5 @@
+MODULE    = nvapi64.dll
+PARENTSRC = ../nvapi
+
+C_SRCS = \
+	nvapi.c
diff --git a/dlls/nvapi64/nvapi64.spec b/dlls/nvapi64/nvapi64.spec
new file mode 100644
index 0000000..f0c054b
--- /dev/null
+++ b/dlls/nvapi64/nvapi64.spec
@@ -0,0 +1,5 @@
+@ cdecl nvapi_QueryInterface(long)
+@ stub DllCanUnloadNow
+@ stub DllGetClassObject
+@ stub DllRegisterServer
+@ stub DllUnregisterServer
diff --git a/include/Makefile.in b/include/Makefile.in
index 6abb2c2..476613a 100644
--- a/include/Makefile.in
+++ b/include/Makefile.in
@@ -482,6 +482,7 @@ SRCDIR_INCLUDES = \
 	ntsecapi.h \
 	ntsecpkg.h \
 	ntstatus.h \
+	nvapi.h \
 	objbase.h \
 	objsel.h \
 	odbcinst.h \
diff --git a/include/nvapi.h b/include/nvapi.h
new file mode 100644
index 0000000..4204256
--- /dev/null
+++ b/include/nvapi.h
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2015 Michael Müller
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
+ */
+
+#ifndef __WINE_NVAPI_H
+#define __WINE_NVAPI_H
+
+#include "pshpack8.h"
+
+typedef unsigned char NvU8;
+typedef unsigned int NvU32;
+
+#define NvAPI_Status int
+
+#define NVAPI_OK 0
+#define NVAPI_ERROR -1
+#define NVAPI_INVALID_ARGUMENT -5
+#define NVAPI_INVALID_HANDLE -8
+#define NVAPI_INCOMPATIBLE_STRUCT_VERSION -9
+#define NVAPI_EXPECTED_PHYSICAL_GPU_HANDLE -101
+#define NVAPI_STEREO_NOT_INITIALIZED -140
+#define NVAPI_UNREGISTERED_RESOURCE -170
+
+#define NVAPI_SHORT_STRING_MAX 64
+#define NVAPI_MAX_PHYSICAL_GPUS 64
+
+typedef char NvAPI_ShortString[NVAPI_SHORT_STRING_MAX];
+
+#define MAKE_NVAPI_VERSION(type,version) (NvU32)(sizeof(type) | ((version)<<16))
+
+typedef void *NvPhysicalGpuHandle;
+typedef void *NvDisplayHandle;
+typedef void *StereoHandle;
+
+typedef struct
+{
+    NvU32              version;
+    NvU32              drvVersion;
+    NvU32              bldChangeListNum;
+    NvAPI_ShortString  szBuildBranchString;
+    NvAPI_ShortString  szAdapterString;
+} NV_DISPLAY_DRIVER_VERSION;
+
+#define NV_DISPLAY_DRIVER_VERSION_VER MAKE_NVAPI_VERSION(NV_DISPLAY_DRIVER_VERSION, 1)
+
+/* undocumented stuff */
+typedef struct
+{
+    NvU32 version;
+    NvU32 gpu_count;
+    struct
+    {
+        NvPhysicalGpuHandle gpuHandle;
+        NvU32 unknown2;
+    } gpus[8];
+}NV_UNKNOWN_1;
+
+#define NV_UNKNOWN_1_VER MAKE_NVAPI_VERSION(NV_UNKNOWN_1, 1)
+
+#include "poppack.h"
+
+#endif /* __WINE_NVAPI_H */
-- 
2.4.0

