// VidMem.cpp : Defines the initialization routines for the DLL.
//
// created by Unwinder
/////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include <float.h>
#include <afxdllx.h>

#include <ddraw.h>
/////////////////////////////////////////////////////////////////////////////
#include "VidMem.h"
#include "RivaTunerExports.h"
#include "MonitoringSourceDesc.h"
/////////////////////////////////////////////////////////////////////////////
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
/////////////////////////////////////////////////////////////////////////////
static AFX_EXTENSION_MODULE VidMemDLL	= { NULL, NULL };
/////////////////////////////////////////////////////////////////////////////
LPDIRECTDRAW			g_lpdd				= NULL;
LPDIRECTDRAW4			g_lpdd4				= NULL;
OSVERSIONINFO			g_osVersion			= {0};

BOOL					g_bNVIDIA			= FALSE;
/////////////////////////////////////////////////////////////////////////////
extern "C" int APIENTRY
DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
{
	UNREFERENCED_PARAMETER(lpReserved);

	if (dwReason == DLL_PROCESS_ATTACH)
	{
		if (!AfxInitExtensionModule(VidMemDLL, hInstance))
			return 0;

		g_osVersion.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);
		GetVersionEx(&g_osVersion);

		new CDynLinkLibrary(VidMemDLL);
	}
	else if (dwReason == DLL_PROCESS_DETACH)
	{
		AfxTermExtensionModule(VidMemDLL);

		//release DirectDraw interfaces

		if (g_lpdd4)
			g_lpdd4->Release();
		g_lpdd4 = NULL;

		if (g_lpdd)
			g_lpdd->Release();
		g_lpdd = NULL;
	}

	return 1;
}
/////////////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get a number of
// data sources in this plugin
/////////////////////////////////////////////////////////////////////////////
VIDMEM_API DWORD GetSourcesNum()
{
	if ((g_osVersion.dwPlatformId	== VER_PLATFORM_WIN32_NT) &&
  		(g_osVersion.dwMajorVersion >= 6))
		//Videomemory is virtualized under Vista so we cannot use DirectDraw
		//interfaces to calculate videomemory usage
		return 0;

	return 3;
}
/////////////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get descriptor
// for the specified data source
/////////////////////////////////////////////////////////////////////////////
VIDMEM_API BOOL GetSourceDesc(DWORD dwIndex, LPMONITORING_SOURCE_DESC pDesc)
{
	//get host application module handle

	HMODULE hHost = GetModuleHandle(NULL);
	if (!hHost)
		return FALSE;

	//get ptr to host application ID routine and return error if the host doesn't 
	//export it

	//we'll use this condition to verify if we're under RivaTuner v2.0 RC 15.7
	//(older versions are not supported )or any other compatible host

	GET_HOST_APP_ID_PROC pGetHostAppID = (GET_HOST_APP_ID_PROC)GetProcAddress(hHost, "GetHostAppID");
	if (!pGetHostAppID)
		return FALSE;

	//check display adapter vendor if host supports PCI DeviceID reporting

	GET_DEVICE_ID_PROC pGetDeviceID	= (GET_DEVICE_ID_PROC)GetProcAddress(hHost, "GetDeviceID");
	
	if (pGetDeviceID)
	{
		CString strDeviceID = pGetDeviceID();	strDeviceID.MakeUpper();

		DWORD	vendorID, deviceID;
		DWORD	subsysID;
		DWORD	revision;

		sscanf(strDeviceID,"PCI\\VEN_%04x&DEV_%04x&SUBSYS_%08x&REV_%02x", &vendorID, &deviceID, &subsysID, &revision);

		if (vendorID == 0x10DE)
			g_bNVIDIA = TRUE;
	}

	//init DirectDraw interfaces if necessary

	if (!g_lpdd4)
	{
		if (FAILED(DirectDrawCreate(NULL, &g_lpdd, NULL)))
			return FALSE;

		if (FAILED(g_lpdd->QueryInterface(IID_IDirectDraw4, (LPVOID*)&g_lpdd4)))
		{
			g_lpdd->Release();
			return FALSE; 
		}
	}

	switch (dwIndex)
	{
	case 0:
		strcpy(pDesc->szName	, "Videomemory usage");
		strcpy(pDesc->szDim		, "MB");
		if (pDesc->dwVersion >= 0x00010002)
			strcpy(pDesc->szGroup, "MEM");

		pDesc->fltMaxLimit	= 512.0f;
		pDesc->fltMinLimit	= 0.0f;
		pDesc->fltGridDim	= 10.0f;

		if (g_bNVIDIA)
			strcpy(pDesc->szDesc, "Videomemory usage reported by IDirectDraw4 interface (NVIDIA)");
		else
			strcpy(pDesc->szDesc, "Videomemory usage reported by IDirectDraw4 interface (native)");

		return TRUE;

	case 1:
		strcpy(pDesc->szName	, "Local videomemory usage");
		strcpy(pDesc->szDim		, "MB");
		if (pDesc->dwVersion >= 0x00010002)
			strcpy(pDesc->szGroup, "MEM");

		pDesc->fltMaxLimit	= 512.0f;
		pDesc->fltMinLimit	= 0.0f;
		pDesc->fltGridDim	= 10.0f;

		strcpy(pDesc->szDesc, "Local videomemory usage reported by IDirectDraw4 interface");

		return TRUE;

	case 2:
		strcpy(pDesc->szName	, "Non-local videomemory usage");
		strcpy(pDesc->szDim		, "MB");
		if (pDesc->dwVersion >= 0x00010002)
			strcpy(pDesc->szGroup, "MEM");

		pDesc->fltMaxLimit	= 512.0f;
		pDesc->fltMinLimit	= 0.0f;
		pDesc->fltGridDim	= 10.0f;

		strcpy(pDesc->szDesc, "Non-local videomemory usage reported by IDirectDraw4 interface");

		return TRUE;
	}

	return FALSE;
}
//////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to get capabilities
// for the specified data source
//////////////////////////////////////////////////////////////////////
VIDMEM_API BOOL GetSourceCaps(DWORD dwIndex, DWORD dwCaps, LPDWORD lpResult)
{
	//all sources are system wide monitoring so it is safe to ignore passed
	//index and check just the capability

	switch (dwCaps)
	{
	case  MONITORING_SOURCE_CAPS_SYSTEM_WIDE:
		*lpResult = 1;
		return TRUE;
	}

	return FALSE;
}
/////////////////////////////////////////////////////////////////////////////
// This exported function is called by RivaTuner to poll data sources
/////////////////////////////////////////////////////////////////////////////
VIDMEM_API FLOAT GetSourceData(DWORD dwIndex)
{
	switch (dwIndex)
	{
	case 0:
		{
			DWORD dwTotal;
			DWORD dwFree;

			DDSCAPS2 caps; 
			ZeroMemory(&caps, sizeof(caps));

			if (g_bNVIDIA)
				caps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM | DDSCAPS_NONLOCALVIDMEM;
			else
				caps.dwCaps = DDSCAPS_VIDEOMEMORY;

			if (SUCCEEDED(g_lpdd4->GetAvailableVidMem(&caps, &dwTotal, &dwFree)))
				return (dwTotal - dwFree) / 1048576.0f;

			break;
		}

	case 1:
		{
			DWORD dwTotal;
			DWORD dwFree;

			DDSCAPS2 caps; 
			ZeroMemory(&caps, sizeof(caps));

 			caps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_LOCALVIDMEM; 

			if (SUCCEEDED(g_lpdd4->GetAvailableVidMem(&caps, &dwTotal, &dwFree)))
				return (dwTotal - dwFree) / 1048576.0f;

			break;
		}

	case 2:
		{
			DWORD dwTotal;
			DWORD dwFree;

			DDSCAPS2 caps; 
			ZeroMemory(&caps, sizeof(caps));

 			caps.dwCaps = DDSCAPS_VIDEOMEMORY | DDSCAPS_NONLOCALVIDMEM; 

			if (SUCCEEDED(g_lpdd4->GetAvailableVidMem(&caps, &dwTotal, &dwFree)))
				return (dwTotal - dwFree) / 1048576.0f;

			break;
		}
	}

	return FLT_MAX;
}
/////////////////////////////////////////////////////////////////////////////
