#include "StdAfx.h"
#include "SystemAnalysis.h"
#include "Helpers.h"

#include <algorithm>



using namespace std;



namespace 
{



	class CSystemInfo
	{
	public:
		static CSystemInfo& GetInstance();

		~CSystemInfo();
		void GetDisplayModes( DispModesCol& colDispModes );
		int GetCPUQuality();
		int GetGPUQuality();
		int GetSystemMem();
		int GetVideoMem();
		bool PS20Supported();

	private:
		CSystemInfo();
		void CollectDisplayModes();
		void CheckCPUQuality();
		void CheckGPUQuality();
		void CheckSystemMem();
		void CheckVideoMem();
		void CheckPS20Support();
		
	private:
		HMODULE m_hD3D9;
		IDirect3D9* m_pD3D9;
		IDirectDraw7* m_pDD7;
		HMODULE m_hDDraw;
		DispModesCol m_colDispModes;
		int m_iCPUQuality;
		int m_iGPUQuality;
		int m_iSystemMem;
		int m_iVideoMem;
		bool m_bPS20Supported;
	};



	CSystemInfo::CSystemInfo()
	: m_hD3D9( 0 )
	, m_pD3D9( 0 )
	, m_hDDraw( 0 )
	, m_pDD7( 0 )
	, m_colDispModes()
	, m_iCPUQuality( 0 )
	, m_iGPUQuality( 0 )
	, m_iSystemMem( 0 )
	, m_iVideoMem( 0 )
	, m_bPS20Supported( false )
	{
		// load d3d9 dll
		m_hD3D9 = LoadLibrary( _T( "d3d9.dll" ) );
		if( 0 != m_hD3D9 )
		{
			// create D3D9 interface
			typedef IDirect3D9* (WINAPI *FPDirect3DCreate9) ( UINT SDKVersion );
			FPDirect3DCreate9 pfDirect3DCreate9( (FPDirect3DCreate9) GetProcAddress( m_hD3D9, "Direct3DCreate9" ) );
			if( 0 != pfDirect3DCreate9 )
			{
				m_pD3D9 = pfDirect3DCreate9( D3D_SDK_VERSION );
			}
		}

		// load dd7 dll
		m_hDDraw = LoadLibrary( _T( "ddraw.dll" ) );
		if( 0 != m_hDDraw )
		{
			// get DD7 interface
			typedef HRESULT (WINAPI *FPDirectDrawCreateEx) ( GUID* lpGUID, void* lplpDD, REFIID iid, IUnknown* pUnkOuter );	
			FPDirectDrawCreateEx pfDirectDrawCreateEx( (FPDirectDrawCreateEx) GetProcAddress( m_hDDraw, "DirectDrawCreateEx" ) );			
			if( 0 != pfDirectDrawCreateEx )
			{
				pfDirectDrawCreateEx( 0, &m_pDD7, IID_IDirectDraw7, 0 );
			}
		}

		// perform analysis
		CollectDisplayModes();
		CheckCPUQuality();
		CheckGPUQuality();
		CheckSystemMem();
		CheckVideoMem();		
		CheckPS20Support();
	}



	CSystemInfo::~CSystemInfo()
	{
		SafeReleaseI( m_pD3D9 );
		if( 0 != m_hD3D9 )
		{
			FreeLibrary( m_hD3D9 );
			m_hD3D9 = 0;
		}

		SafeReleaseI( m_pDD7 );
		if( 0 != m_hDDraw )
		{
			FreeLibrary( m_hDDraw );
			m_hD3D9 = 0;
		}
	}



	CSystemInfo& 
	CSystemInfo::GetInstance()
	{
		static CSystemInfo s_kInstance;
		return( s_kInstance );
	}



	inline bool
	SortModesAscending( const SDispMode& rhs, const SDispMode& lhs )
	{
		bool bRet( false );
		if( rhs.m_xRes < lhs.m_xRes )
		{
			bRet = true;
		}
		else if( rhs.m_xRes == lhs.m_xRes )
		{
			if( rhs.m_yRes < lhs.m_yRes )
			{
				bRet = true;
			}
			else if( rhs.m_yRes == lhs.m_yRes )
			{
				if( rhs.m_bpp < lhs.m_bpp )
				{
					bRet = true;
				}
			}
		}
		return( bRet );
	}



	void
	CSystemInfo::CollectDisplayModes()
	{
		if( 0 != m_pD3D9 )
		{
			DispModesCol colDispModesTmp;
			D3DFORMAT supportedFormats[] = { D3DFMT_X8R8G8B8, D3DFMT_A8R8G8B8 /*, D3DFMT_X1R5G5B5, D3DFMT_A1R5G5B5, D3DFMT_R5G6B5*/ };
			for( unsigned int i( 0 ); i < sizeof( supportedFormats ) / sizeof( supportedFormats[ 0 ] ); ++i )
			{
				unsigned int nNumModes( m_pD3D9->GetAdapterModeCount( D3DADAPTER_DEFAULT, supportedFormats[ i ] ) );
				for( unsigned int j( 0 ); j < nNumModes; ++j )
				{
					D3DDISPLAYMODE mode;
					if( SUCCEEDED( m_pD3D9->EnumAdapterModes( D3DADAPTER_DEFAULT, supportedFormats[ i ], j, &mode ) ) )
					{
						if( 640 < mode.Width && 480 < mode.Height && 
							( D3DFMT_X8R8G8B8 == mode.Format || D3DFMT_A8R8G8B8 == mode.Format ) )
						{
							colDispModesTmp.push_back( SDispMode( mode.Width, mode.Height, 32 ) );
						}
						else if( 640 < mode.Width && 480 < mode.Height && 
								 ( D3DFMT_X1R5G5B5 == mode.Format || D3DFMT_A1R5G5B5 == mode.Format || D3DFMT_R5G6B5 == mode.Format ) )
						{
							colDispModesTmp.push_back( SDispMode( mode.Width, mode.Height, 16 ) );
						}
					}
				}
			}	

			// sort display modes
			sort( colDispModesTmp.begin(), colDispModesTmp.end(), SortModesAscending );

			// filter duplicates
			DispModesCol::size_type lastUnique( -1 );
			for( DispModesCol::size_type i( 0 ); i < colDispModesTmp.size(); ++i )
			{
				if( -1 == lastUnique || 
					( colDispModesTmp[ lastUnique ].m_xRes != colDispModesTmp[ i ].m_xRes ||
					  colDispModesTmp[ lastUnique ].m_yRes != colDispModesTmp[ i ].m_yRes ||
					  colDispModesTmp[ lastUnique ].m_bpp != colDispModesTmp[ i ].m_bpp ) )
				{
					m_colDispModes.push_back( colDispModesTmp[ i ] );
					lastUnique = i;
				}
			}
		}
	}



#ifdef WIN64
	inline bool IsAMD64()
	{
#ifdef _AMD64_
		return( true );
#else
#error not supported here
#endif
	}
#else
	inline bool
	IsAMD64()
	{
		_asm
		{
			// make sure cpuid is available
			pushfd					// save EFLAGS
			pop eax					// store EFLAGS in EAX
			mov ebx, eax			// save in EBX for later testing
			xor eax, 00200000h		// toggle bit 21
			push eax				// push to stack
			popfd					// save changed EAX to EFLAGS
			pushfd					// push EFLAGS to TOS
			pop eax					// store EFLAGS in EAX
			cmp eax, ebx			// see if bit 21 has changed
			mov eax, 0				// clear eax to return "false"
			jz QUIT					// if no change, no CPUID

			// perform AMD64 detection
			mov eax, 0x80000001		// load argument "Processor Signature and AMD Features" to call cpuid
			cpuid					// call cpuid
			mov eax, edx			// edx contains "AMD Feature Support" flags
			shr eax, 29				// test bit 29 (support for "Long Mode")
			and eax, 0x00000001		// mask out bit for proper return value
			QUIT:
		}
	}
#endif // WIN32



	inline double 
	RDTSC()
	{
#if defined( WIN32 ) && !defined( WIN64 )  
		unsigned int  l, h;
		__asm
		{
			xor   eax, eax		// Required so that VC++ realizes EAX is modified.
			xor   edx, edx		// Required so that VC++ realizes EDX is modified.
			_emit 0x0F			// RDTSC  -  Pentium+ time stamp register to EDX:EAX.
			_emit 0x31			// Use only 32 bits in EAX - even a Ghz cpu would have a 4+ sec period.
			mov   [ l ], eax	// Save low value.
			mov   [ h ], edx	// Save high value.
		}
		return( (double) l +  4294967296.0 * (double) h );
#else
		return( __rdtsc() );
#endif
	}



	double 
	GetCPUClockSpeed()
	{
		// get a copy of the current thread and process priorities
		DWORD priorityClass( GetPriorityClass( GetCurrentProcess() ) );
		int threadPriority( GetThreadPriority( GetCurrentThread() ) );

		LARGE_INTEGER Freq;
		QueryPerformanceFrequency( &Freq );
		double dFrequency( (double) ( (INT64) Freq.HighPart * ( (INT64) 65536 * (INT64) 65536 ) + (INT64) Freq.LowPart ) );

		// make this thread the highest possible priority so we get the best timing
		SetPriorityClass( GetCurrentProcess(), REALTIME_PRIORITY_CLASS );
		SetThreadPriority( GetCurrentThread(), THREAD_PRIORITY_TIME_CRITICAL );

		LARGE_INTEGER c0;
		QueryPerformanceCounter( &c0 );		
		double dTSC0( RDTSC() );

		Sleep( 100 );

		double dTSC1( RDTSC() );		
		LARGE_INTEGER c1;
		QueryPerformanceCounter( &c1 );

		// restore the thread priority
		SetPriorityClass( GetCurrentProcess(), priorityClass );
		SetThreadPriority( GetCurrentThread(), threadPriority ); 

		double dCount0( (double) ( (INT64) c0.HighPart * ( (INT64) 65536 * (INT64) 65536 ) + (INT64) c0.LowPart ) / dFrequency );
		double dCount1( (double) ( (INT64) c1.HighPart * ( (INT64) 65536 * (INT64) 65536 ) + (INT64) c1.LowPart ) / dFrequency );
		double dSecondsPerCycle( ( dCount1 - dCount0 ) / ( dTSC1 - dTSC0 ) ) ;

		return(  1e-6 / dSecondsPerCycle );
	}


	void 
	CSystemInfo::CheckCPUQuality()
	{
		double dMHz( GetCPUClockSpeed() );
		if( false != IsAMD64() )
		{
			dMHz *= 1.5f; // AMD64 with 2 GHz is equal to a 3.0 GHz machine
			dMHz = ( dMHz < 3000.0f ) ? 3000.0f : dMHz;
		}
		
		if( dMHz >= 2900 )
		{
			m_iCPUQuality = 3; // very high spec
		}
		else if( dMHz >= 2500 )
		{
			m_iCPUQuality = 2; // high spec
		}
		else if( dMHz >= 16900 )
		{
			m_iCPUQuality = 1; // medium spec
		}
		else
		{
			m_iCPUQuality = 1; // low spec
		}
	}



	void 
	CSystemInfo::CheckGPUQuality()
	{
		m_iGPUQuality = 0;

		DDDEVICEIDENTIFIER2 DeviceIdentifier;
		memset( &DeviceIdentifier, 0, sizeof( DeviceIdentifier ) );
		if( SUCCEEDED( m_pDD7->GetDeviceIdentifier( &DeviceIdentifier, 0 ) ) )
		{
			switch( DeviceIdentifier.dwVendorId )
			{
			case 0x1002: // ATI
				{
					switch( DeviceIdentifier.dwDeviceId )
					{
					case 0x4c64: m_iGPUQuality = 0; break; // Radeon Mobility 9000
					case 0x4c66: m_iGPUQuality = 0; break; // Radeon Mobility 9000
					case 0x4966: m_iGPUQuality = 0; break; // Radeon 9000
					case 0x496e: m_iGPUQuality = 0; break; // Radeon 9000 - Secondary
					case 0x514d: m_iGPUQuality = 0; break; // Radeon 9100
					case 0x5834: m_iGPUQuality = 0; break; // Radeon 9100 IGP
					case 0x4242: m_iGPUQuality = 0; break; // Radeon 8500 DV
					case 0x4152: m_iGPUQuality = 1; break; // Radeon 9600
					case 0x4172: m_iGPUQuality = 1; break; // Radeon 9600 - Secondary
					case 0x4164: m_iGPUQuality = 1; break; // Radeon 9500 - Secondary
					case 0x4144: m_iGPUQuality = 2; break; // Radeon 9500
					case 0x4e45: m_iGPUQuality = 2; break; // Radeon 9500 Pro / 9700
					case 0x4150: m_iGPUQuality = 1; break; // Radeon 9600 Pro
					case 0x4151: m_iGPUQuality = 1; break; // Radeon 9600
					case 0x4170: m_iGPUQuality = 1; break; // Radeon 9600 Pro - Secondary
					case 0x4171: m_iGPUQuality = 1; break; // Radeon 9600 - Secondary
					case 0x4e46: m_iGPUQuality = 1; break; // Radeon 9600 TX
					case 0x4e66: m_iGPUQuality = 1; break; // Radeon 9600 TX - Secondary
					case 0x4e44: m_iGPUQuality = 3; break; // Radeon 9700 Pro
					case 0x4e64: m_iGPUQuality = 3; break; // Radeon 9700 Pro - Secondary
					case 0x4e65: m_iGPUQuality = 2; break; // Radeon 9500 Pro / 9700 - Secondary
					case 0x4e49: m_iGPUQuality = 3; break; // Radeon 9800
					case 0x4e69: m_iGPUQuality = 3; break; // Radeon 9800 - Secondary
					case 0x4148: m_iGPUQuality = 3; break; // Radeon 9800
					case 0x4168: m_iGPUQuality = 3; break; // Radeon 9800 - Secondary
					case 0x4e48: m_iGPUQuality = 3; break; // Radeon 9800 Pro
					case 0x4e68: m_iGPUQuality = 3; break; // Radeon 9800 Pro - Secondary
					case 0x4e4a: m_iGPUQuality = 3; break; // Radeon 9800 XT
					case 0x4e6a: m_iGPUQuality = 3; break; // Radeon 9800 XT - Secondary
					case 0x5960: m_iGPUQuality = 1; break; // Radeon 9200 Pro
					case 0x5940: m_iGPUQuality = 1; break; // Radeon 9200 Pro - Secondary
					case 0x5961: m_iGPUQuality = 0; break; // Radeon 9200
					case 0x5941: m_iGPUQuality = 1; break; // Radeon 9200 - Secondary
					case 0x5964: m_iGPUQuality = 1; break; // Radeon 9200SE
					case 0x514c: m_iGPUQuality = 0; break; // Radeon 8500
					case 0x514e: m_iGPUQuality = 0; break; // Radeon 8500
					case 0x514f: m_iGPUQuality = 0; break; // Radeon 8500
					case 0x4136: m_iGPUQuality = 0; break; // IGP 320
					case 0x4137: m_iGPUQuality = 0; break; // IGP 340
					case 0x4a49: m_iGPUQuality = 3; break; // Radeon X800 Pro
					case 0x4a4a: m_iGPUQuality = 3; break; // Radeon X800 SE
					case 0x4a4b: m_iGPUQuality = 3; break; // Radeon X800
					case 0x4a4c: m_iGPUQuality = 3; break; // Radeon X800 Series
					case 0x4a50: m_iGPUQuality = 3; break; // Radeon X800 XT
					case 0x4a69: m_iGPUQuality = 3; break; // Radeon X800 Pro Secondary
					case 0x4a6a: m_iGPUQuality = 3; break; // Radeon X800 SE Secondary
					case 0x4a6b: m_iGPUQuality = 3; break; // Radeon X800 Secondary
					case 0x4a6c: m_iGPUQuality = 3; break; // Radeon X800 Series Secondary
					case 0x4a70: m_iGPUQuality = 3; break; // Radeon X800 XT Secondary
					}
				}

			case 0x10b4: // NVIDIA
			case 0x12d2:
			case 0x10de:
				{
					switch( DeviceIdentifier.dwDeviceId )
					{
					case 0x0152: m_iGPUQuality = 0; break; // GeForce2 Ultra
					case 0x0153: m_iGPUQuality = 0; break; // Quadro2 Pro
					case 0x0170: m_iGPUQuality = 0; break; // GeForce4 MX 460
					case 0x0171: m_iGPUQuality = 0; break; // GeForce4 MX 440
					case 0x0172: m_iGPUQuality = 0; break; // GeForce4 MX 420
					case 0x0173: m_iGPUQuality = 0; break; // GeForce4 MX 440-SE
					case 0x0174: m_iGPUQuality = 0; break; // GeForce4 Go 440
					case 0x0175: m_iGPUQuality = 0; break; // GeForce4 Go 420
					case 0x0176: m_iGPUQuality = 0; break; // GeForce4 Go 420
					case 0x0178: m_iGPUQuality = 0; break; // Quadro4 550 XGL
					case 0x0179: m_iGPUQuality = 0; break; // GeForce4 Go 440
					case 0x017a: m_iGPUQuality = 0; break; // Quadro NVS
					case 0x017b: m_iGPUQuality = 0; break; // Quadro 550 XGL
					case 0x0181: m_iGPUQuality = 0; break; // GeForce4 MX 440 with AGP8X
					case 0x0182: m_iGPUQuality = 0; break; // GeForce4 MX 440SE with AGP8X
					case 0x0183: m_iGPUQuality = 0; break; // GeForce4 MX 420 with AGP8X
					case 0x0188: m_iGPUQuality = 0; break; // Quadro4 580 XGL
					case 0x018a: m_iGPUQuality = 0; break; // Quadro NVS with AGP8X
					case 0x018b: m_iGPUQuality = 0; break; // Quadro4 380 XGL
					case 0x01f0: m_iGPUQuality = 0; break; // GeForce4 MX Integrated GPU (nForce2)
					case 0x0200: m_iGPUQuality = 0; break; // GeForce3
					case 0x0201: m_iGPUQuality = 0; break; // GeForce3 Ti200
					case 0x0202: m_iGPUQuality = 0; break; // GeForce3 Ti500
					case 0x0203: m_iGPUQuality = 0; break; // Quadro DCC
					case 0x0250: m_iGPUQuality = 1; break; // GeForce4 Ti 4600
					case 0x0251: m_iGPUQuality = 1; break; // GeForce4 Ti 4400
					case 0x0253: m_iGPUQuality = 0; break; // GeForce4 Ti 4200
					case 0x0258: m_iGPUQuality = 0; break; // Quadro4 900 XGL
					case 0x0259: m_iGPUQuality = 0; break; // Quadro4 750 XGL
					case 0x025b: m_iGPUQuality = 0; break; // Quadro4 700 XGL
					case 0x0280: m_iGPUQuality = 1; break; // GeForce4 Ti 4800
					case 0x0281: m_iGPUQuality = 0; break; // GeForce4 Ti4200 with AGP8X
					case 0x0282: m_iGPUQuality = 0; break; // GeForce4 Ti 4800 SE
					case 0x0288: m_iGPUQuality = 0; break; // Quadro4 980 XGL
					case 0x0289: m_iGPUQuality = 0; break; // Quadro4 780 XGL
					case 0x02a0: m_iGPUQuality = 0; break; // GeForce3 XBOX
					case 0x0301: m_iGPUQuality = 1; break; // GeForce FX 5800 Ultra
					case 0x0302: m_iGPUQuality = 1; break; // GeForce FX 5800
					case 0x0308: m_iGPUQuality = 1; break; // Quadro FX 2000
					case 0x0309: m_iGPUQuality = 1; break; // Quadro FX 1000
					case 0x030a: m_iGPUQuality = 1; break; // ICE FX 2000
					case 0x0311: m_iGPUQuality = 1; break; // GeForce FX 5600 Ultra
					case 0x0312: m_iGPUQuality = 1; break; // GeForce FX 5600
					case 0x0313: m_iGPUQuality = 1; break; // NV31
					case 0x0314: m_iGPUQuality = 1; break; // GeForce FX 5600XT
					case 0x031a: m_iGPUQuality = 1; break; // GeForce FX Go5600
					case 0x0321: m_iGPUQuality = 1; break; // GeForce FX 5200 Ultra
					case 0x0322: m_iGPUQuality = 1; break; // GeForce FX 5200
					case 0x0323: m_iGPUQuality = 1; break; // GeForce FX 5200SE
					case 0x032b: m_iGPUQuality = 1; break; // Quadro FX 500
					case 0x032f: m_iGPUQuality = 1; break; // NV34GL
					case 0x0330: m_iGPUQuality = 3; break; // GeForce FX 5900 Ultra
					case 0x0331: m_iGPUQuality = 2; break; // GeForce FX 5900
					case 0x0332: m_iGPUQuality = 2; break; // NV35
					case 0x0333: m_iGPUQuality = 3; break; // GeForce FX 5950 Ultra
					case 0x0338: m_iGPUQuality = 2; break; // Quadro FX 3000
					case 0x0341: m_iGPUQuality = 2; break; // GeForce FX 5700 Ultra
					case 0x0342: m_iGPUQuality = 2; break; // GeForce FX 5700
					case 0x034e: m_iGPUQuality = 1; break; // Quadro FX 1100
					case 0x0040:													 // GeForce 6800 Ultra
					case 0x0041:													 // GeForce 6800
					case 0x0042:
					case 0x0043:
					case 0x0044:
					case 0x0045:
					case 0x0046:
					case 0x0047:
					case 0x0048:
					case 0x0049:													 // NV40GL
					case 0x004a:
					case 0x004b:
					case 0x004c:
					case 0x004d:
					case 0x004e:													 // NV40GL
					case 0x004f: m_iGPUQuality = 3; break; // NV40 (GeForce ???)
					case 0x00f9: m_iGPUQuality = 3; break; // GeForce 6800 Ultra
					case 0x00fa: m_iGPUQuality = 2; break; // GeForce PCX 5750
					case 0x00fb: m_iGPUQuality = 2; break; // GeForce PCX 5900
					case 0x00fc: m_iGPUQuality = 1; break; // GeForce PCX 5300
					case 0x00fd: m_iGPUQuality = 2; break; // Quadro PCI-E Series
					case 0x00fe: m_iGPUQuality = 2; break; // Quadro FX 1300
					case 0x00ff: m_iGPUQuality = 1; break; // GeForce PCX 4300
					}
				}
			}
		}
	}



	void 
	CSystemInfo::CheckSystemMem()
	{
		// get total physical system memory in MB
		MEMORYSTATUS sMemStat;
		GlobalMemoryStatus( &sMemStat );		
		m_iSystemMem = (int) ( sMemStat.dwTotalPhys >> 20 );
	}



	void 
	CSystemInfo::CheckVideoMem()
	{
		// get total physical video memory in MB
		if( 0 != m_pDD7 )
		{
			DDSCAPS2 sDDSCaps2;
			ZeroMemory( &sDDSCaps2, sizeof( sDDSCaps2 ) );
			sDDSCaps2.dwCaps = DDSCAPS_LOCALVIDMEM; 

			DWORD dwTotalVideoMemory( 0 );
			m_pDD7->GetAvailableVidMem( &sDDSCaps2, &dwTotalVideoMemory, 0 );
			m_iVideoMem = (int) ( dwTotalVideoMemory >> 20 );
		}
	}



	void 
	CSystemInfo::CheckPS20Support()
	{
		m_bPS20Supported = false;
		if( 0 != m_pD3D9 )
		{
			// retrieve caps of device to check pixel shader support
			D3DCAPS9 sCaps;
			ZeroMemory( &sCaps, sizeof( sCaps ) );
			if( SUCCEEDED( m_pD3D9->GetDeviceCaps( D3DADAPTER_DEFAULT, D3DDEVTYPE_HAL, &sCaps ) ) )
			{
				if( D3DSHADER_VERSION_MAJOR( sCaps.PixelShaderVersion ) >= 2 )
				{
					m_bPS20Supported = true;
				}
			}
		}
	}



	void 
	CSystemInfo::GetDisplayModes( DispModesCol& colDispModes )
	{
		colDispModes = m_colDispModes;
	}



	int 
	CSystemInfo::GetCPUQuality()
	{
		return( m_iCPUQuality );
	}



	int 
	CSystemInfo::GetGPUQuality()
	{
		return( m_iGPUQuality );
	}
	


	int 
	CSystemInfo::GetSystemMem()
	{
		return( m_iSystemMem );
	}



	int 
	CSystemInfo::GetVideoMem()
	{
		return( m_iVideoMem );
	}



	bool
	CSystemInfo::PS20Supported()
	{
		return( m_bPS20Supported );
	}



} // end namespace



void 
GetDisplayModes( DispModesCol& colDispModes )
{
	CSystemInfo::GetInstance().GetDisplayModes( colDispModes );
}



int 
GetCPUQuality()
{
	return( CSystemInfo::GetInstance().GetCPUQuality() );
}



int 
GetGPUQuality()
{
	return( CSystemInfo::GetInstance().GetGPUQuality() );
}



int 
GetSystemMem()
{
	return( CSystemInfo::GetInstance().GetSystemMem() );
}



int 
GetVideoMem()
{
	return( CSystemInfo::GetInstance().GetVideoMem() );
}



bool 
IsPS20Supported()
{
	return( CSystemInfo::GetInstance().PS20Supported() );
}



int
DetermineMachineSpec( bool bCheckCPU, bool bCheckGPU, bool bCheckSysMem, bool bCheckVidMem )
{
	int iCPUQuality( GetCPUQuality() ); // cpu clock speed in MHz (use safety net for comparisons; i.e. -100 MHz)
	int iGPUQuality( GetGPUQuality() ); // quality of detected GPU ranging from 0 ((s)low) to 3 (killer)
	int iSystemMem( GetSystemMem() );   // physical amount of RAM in MB (use safety net for comparisons; i.e. -12 for 512 MB, 24 for 1024 MB)
	int iVideoMem( GetVideoMem() );     // on board video memory in MB (use safety net for comparisons; i.e. -32 for 256 MB, -16 for 128 MB)

	// perform detection
	if( ( iCPUQuality >= 3 * (int) bCheckCPU ) && ( iGPUQuality >= 3 * (int) bCheckGPU ) &&
		( iSystemMem >= 1000 * (int) bCheckSysMem ) && ( iVideoMem >= 224  * (int) bCheckVidMem ) )
	{
		return( 3 ); // very high spec
	}
	else if( ( iCPUQuality >= 2 * (int) bCheckCPU ) && ( iGPUQuality >= 2 * (int) bCheckGPU ) &&
		( iSystemMem >= 750 * (int) bCheckSysMem ) && ( iVideoMem >= 224 * (int) bCheckVidMem ) )
	{
		return( 2 ); // high spec
	}
	else if( ( iCPUQuality >= 1 * (int) bCheckCPU ) && ( iGPUQuality >= 1 * (int) bCheckGPU ) &&
		( iSystemMem >= 500 * (int) bCheckSysMem ) && ( iVideoMem >= 112 * (int) bCheckVidMem ) )
	{
		return( 1 ); // medium spec
	}
	else
	{
		return( 0 ); // low spec
	}
}
