/*==LICENSE==*

CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011  Cyan Worlds, Inc.

This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.

This program 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 General Public License for more details.

You should have received a copy of the GNU General Public License
along with this program.  If not, see <http://www.gnu.org/licenses/>.

Additional permissions under GNU GPL version 3 section 7

If you modify this Program, or any covered work, by linking or
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
(or a modified version of those libraries),
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
licensors of this Program grant you additional
permission to convey the resulting work. Corresponding Source for a
non-source form of such a combination shall include the source code for
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
work.

You can contact Cyan Worlds, Inc. by email legal@cyan.com
 or by snail mail at:
      Cyan Worlds, Inc.
      14617 N Newport Hwy
      Mead, WA   99021

*==LICENSE==*/
#include "plDXEnumerate.h"
#include <ddraw.h>

#include "hsGDDrawDllLoad.h"
#include "hsG3DDeviceSelector.h"


//// Local Typedefs ///////////////////////////////////////////////////////////

typedef LPDIRECT3D9 (WINAPI * Direct3DCreateProc)( UINT sdkVersion );

const uint8_t hsGDirect3DTnLEnumerate::kNumDisplayFormats = 6;
const D3DFORMAT hsGDirect3DTnLEnumerate::kDisplayFormats[] = 
{
    D3DFMT_A1R5G5B5,
        D3DFMT_A2B10G10R10,
        D3DFMT_A8R8G8B8,
        D3DFMT_R5G6B5,
        D3DFMT_X1R5G5B5,
        D3DFMT_X8R8G8B8,
};

HRESULT hsGDirect3DTnLEnumerate::SelectFromDevMode(const hsG3DDeviceRecord* devRec, const hsG3DDeviceMode* devMode)
{

    int i;
    for( i = 0; i < GetNumDrivers(); i++ )
    {
        if( !stricmp(GetDriver(i)->fAdapterInfo.Description, devRec->GetDriverDesc()) )
        {
            int j;
            for( j = 0; j < GetDriver(i)->fDevices.GetCount(); j++ )
            {
                if( !stricmp(GetDriver(i)->fDevices[j].fStrName, devRec->GetDeviceDesc()) )
                {
                    SetCurrentDriver(GetDriver(i));
                    SetCurrentDevice(&GetDriver(i)->fDevices[j]);
                    D3DEnum_SelectDefaultMode(
                        devMode->GetWidth(),
                        devMode->GetHeight(),
                        devMode->GetColorDepth());
                    return false;
                }
            }
        }
    }
    char errStr[256];

    sprintf(errStr, "Can't find requested device - %s:%s:%s:%s:%s",
        devRec->GetG3DDeviceTypeName(), 
        devRec->GetDriverDesc(), 
        devRec->GetDriverName(),
        devRec->GetDriverVersion(),
        devRec->GetDeviceDesc());

    DWORD enumFlags = 0;
    int width = devMode->GetWidth();
    int height = devMode->GetHeight();
    int colorDepth = devMode->GetColorDepth();
    // for a window, take whatever colordepth we can get.
    if( !colorDepth )
        enumFlags |= D3DENUM_CANWINDOW;
    enumFlags |= D3DENUM_TNLHAL;
#ifdef HS_ALLOW_D3D_REF_DRIVER
    enumFlags |= D3DENUM_REFERENCERAST;
#endif

    D3DEnum_SelectDefaultDriver(enumFlags);

    // If we didn't get what we want, try for anything.
    if( !GetCurrentDriver() || !GetCurrentDevice() )
    {
        enumFlags = colorDepth ? 0 : D3DENUM_CANWINDOW;
        D3DEnum_SelectDefaultDriver(enumFlags);
    }
    if( !GetCurrentDriver() || !GetCurrentDevice() )
        D3DEnum_SelectDefaultDriver(0);
    if( !GetCurrentDriver() || !GetCurrentDevice() )
    {
        if( !*GetEnumeErrorStr() )
            SetEnumeErrorStr("Error finding device");
        return true;
    }   
    D3DEnum_SelectDefaultMode(width, height, colorDepth);
    if( !GetCurrentMode() )
    {
        if( !*GetEnumeErrorStr() )
            SetEnumeErrorStr("Error finding mode");
        return true;
    }

    return false;
}

HRESULT hsGDirect3DTnLEnumerate::D3DEnum_SelectDefaultMode(int width, int height, int depth)
{
    hsAssert(GetCurrentDriver() && GetCurrentDevice(), "Must have selected device already");

    BOOL windowed = false;
    if (depth == 0)
    {
        // Legacy code writes out 0 bit depth to mean windowed
        windowed = true;
        depth = 32;
    }

    D3DEnum_DeviceInfo* device = GetCurrentDevice();
    int i;
    for( i = 0; i < device->fModes.GetCount(); i++ )
    {
        D3DEnum_ModeInfo* mode = &device->fModes[i];
        if (mode->fWindowed != windowed)
            continue;

        if( depth )
        {
            if( width < mode->fDDmode.Width )
                continue;
            if( height < mode->fDDmode.Height )
                continue;
        }
        if( depth < mode->fBitDepth )
            continue;

        if( GetCurrentMode() )
        {
            D3DEnum_ModeInfo* curMode = GetCurrentDriver()->fCurrentMode;
            if( depth )
            {
                if( curMode->fDDmode.Width > mode->fDDmode.Width )
                    continue;
                if( curMode->fDDmode.Height > mode->fDDmode.Height )
                    continue;
            }
            if( curMode->fBitDepth > mode->fBitDepth )
                continue;

        }

        SetCurrentMode(mode);
    }
    return S_OK;
}

//-----------------------------------------------------------------------------
// Name: D3DEnum_SelectDefaultDriver()
// Desc: Picks a default driver according to the passed in flags.
//-----------------------------------------------------------------------------
HRESULT hsGDirect3DTnLEnumerate::D3DEnum_SelectDefaultDriver( DWORD dwFlags )
{


    // If a specific driver was requested, perform that search here
    if( dwFlags & D3DENUM_MASK )
    {
        int i;
        for( i = 0; i < fDrivers.GetCount(); i++ )
        {
            D3DEnum_DriverInfo* pDriver = &fDrivers[i];
            int j;
            for( j = 0; j < pDriver->fDevices.GetCount(); j++ )
            {
                D3DEnum_DeviceInfo* pDevice = &pDriver->fDevices[j]; 
                BOOL bFound = FALSE;

                if( pDevice->fDDType == D3DDEVTYPE_REF )
                {
                    if( dwFlags & D3DENUM_REFERENCERAST )
                        bFound = TRUE;
                }
                else if( pDevice->fDDType == D3DDEVTYPE_HAL && 
                    pDevice->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
                {
                    if( dwFlags & D3DENUM_TNLHAL )
                        bFound = TRUE;
                }
                else
                {
                    if( dwFlags & D3DENUM_CANWINDOW )
                    {
                        if( (pDriver == &fDrivers[0])
                            &&( pDevice->fDDCaps.Caps2 & DDCAPS2_CANRENDERWINDOWED ) )
                        {
                            if( ( pDevice->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
                                ^ !(dwFlags & D3DENUM_TNLHAL) )
                                bFound = TRUE;
                        }
                    }
                    else
                        if( dwFlags & D3DENUM_PRIMARYHAL )
                        {
                            if( pDriver == &fDrivers[0] )
                                bFound = TRUE;
                        }
                        else
                            if( dwFlags & D3DENUM_SECONDARYHAL )
                            {
                                if( pDriver != &fDrivers[0] )
                                    bFound = TRUE;
                            }
                }

                if( bFound )
                {
                    SetCurrentDriver(pDriver);
                    SetCurrentDevice(pDevice);
                    return S_OK;
                }
            }
        }
        return D3DENUMERR_NOTFOUND;
    }

    int i;
    for( i = 0; i < fDrivers.GetCount(); i++ )
    {
        D3DEnum_DriverInfo* pDriver = &fDrivers[i];
        int j;
        for( j = 0; j < pDriver->fDevices.GetCount(); j++ )
        {
            D3DEnum_DeviceInfo* pDevice = &pDriver->fDevices[j]; 
            if( !pDevice->fIsHardware )
                continue;

            SetCurrentDriver(pDriver);
            SetCurrentDevice(pDevice);

            return S_OK;
        }
    }

    // No compatible devices were found. Return an error code
    return D3DENUMERR_NOCOMPATIBLEDEVICES;
}


//// Constructor //////////////////////////////////////////////////////////////
//
//  Inits the enumeration and builds our list of devices/whatever.

hsGDirect3DTnLEnumerate::hsGDirect3DTnLEnumerate()
{
    memset( &fEnumeErrorStr[0], 0x00, sizeof(fEnumeErrorStr) );

    fCurrentDriver = NULL;      // The selected DD driver
    fDrivers.Reset();       // List of DD drivers


    /// New DX Enumeration

    // Get a pointer to the creation function
    if( hsGDDrawDllLoad::GetD3DDll() == nil )
    {
        strcpy( fEnumeErrorStr, "Cannot load Direct3D driver!" );
        return; 
    }

    Direct3DCreateProc      procPtr;
    procPtr = (Direct3DCreateProc)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "Direct3DCreate9" );
    if( procPtr == nil )
    {
        strcpy( fEnumeErrorStr, "Cannot load D3D Create Proc!" );
        return;
    }

    // Create a D3D object to use
    IDirect3D9 *pD3D = procPtr( D3D_SDK_VERSION );
    if( pD3D == nil )
    {
        strcpy( fEnumeErrorStr, "Cannot load DirectX!" );
        return;
    }

    /// Loop through the "adapters" (we don't call them drivers anymore)
    UINT    iAdapter;
    for( iAdapter = 0; iAdapter < pD3D->GetAdapterCount(); iAdapter++ )
    {
        D3DEnum_DriverInfo* newDriver = fDrivers.Push();
        ZeroMemory( newDriver, sizeof( *newDriver ) );

        // Copy data to a device info structure
        D3DADAPTER_IDENTIFIER9      adapterInfo;
        pD3D->GetAdapterIdentifier( iAdapter, 0, &adapterInfo );
        pD3D->GetAdapterDisplayMode( iAdapter, &newDriver->fDesktopMode );

        memcpy( &newDriver->fAdapterInfo, &adapterInfo, sizeof( adapterInfo ) );
        strncpy( newDriver->fStrName, adapterInfo.Driver, 39 );
        strncpy( newDriver->fStrDesc, adapterInfo.Description, 39 );
        newDriver->fGuid = adapterInfo.DeviceIdentifier;
        newDriver->fMemory = 16 * 1024 * 1024;      /// Simulate 16 MB

        /// Do the mode and device enumeration for this adapter
        IEnumAdapterDevices( pD3D, iAdapter, newDriver );       
    }

    // Cleanup
    pD3D->Release();
}

//// IEnumAdapterDevices //////////////////////////////////////////////////////
//
//  DirectX: Enumerates all the modes for a given adapter, then using the
//  two faked modes for HAL and REF, attaches the modes to each "device" that
//  can support them. 

void    hsGDirect3DTnLEnumerate::IEnumAdapterDevices( IDirect3D9 *pD3D, UINT iAdapter, D3DEnum_DriverInfo *drivInfo )
{
    // A bit backwards from DX8... First we have to go through our list of formats and check for validity.
    // Then we can enum through the modes for each format.

    const DWORD numDeviceTypes = 2;
    const TCHAR* strDeviceDescs[] = { "HAL", "REF" };
    const D3DDEVTYPE deviceTypes[] = { D3DDEVTYPE_HAL, D3DDEVTYPE_REF };

    BOOL *formatWorks = new BOOL[kNumDisplayFormats + 1];       // One for each format
    DWORD *behavior = new DWORD[kNumDisplayFormats + 1];
    UINT iDevice;
    for (iDevice = 0; iDevice < numDeviceTypes; iDevice++)
    {
        D3DEnum_DeviceInfo  *deviceInfo = drivInfo->fDevices.Push();
        ZeroMemory(deviceInfo, sizeof(*deviceInfo));

        pD3D->GetDeviceCaps(iAdapter, deviceTypes[iDevice], &deviceInfo->fDDCaps);
        strncpy(deviceInfo->fStrName, strDeviceDescs[iDevice], 39);
        deviceInfo->fDDType = deviceTypes[iDevice];
        deviceInfo->fIsHardware = deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWRASTERIZATION;

        /// Loop through the formats, checking each against this device to see
        /// if it will work. If so, add all modes matching that format
        uint8_t iFormat;
        for (iFormat = 0; iFormat < kNumDisplayFormats + 1; iFormat++ )
        {
            // the desktop format gets to be first, everything else is nudged over one.
            D3DFORMAT currFormat = (iFormat == 0 ? drivInfo->fDesktopMode.Format : kDisplayFormats[iFormat - 1]);
            formatWorks[iFormat] = FALSE;

            int bitDepth = IGetDXBitDepth(currFormat);
            if (bitDepth == 0)
                continue;       // Don't like this mode, skip it

            /// Can it be used as a render target?
            if (FAILED(pD3D->CheckDeviceType(iAdapter, deviceTypes[iDevice], 
                currFormat,
                currFormat, 
                FALSE)))
                continue;   // Nope--skip it

            if (deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT)
            {
                /// Confirm that HW vertex processing works on this device
                if (deviceInfo->fDDCaps.DevCaps & D3DDEVCAPS_PUREDEVICE)
                {
#if 0
                    behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING |
                        D3DCREATE_PUREDEVICE;
#else
                    behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING;
#endif
                    if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat],
                        currFormat)))
                    {
                        formatWorks[iFormat] = TRUE;
                    }
                }

                if (!formatWorks[iFormat])
                {
                    /// HW vertex & Pure didn't work--just try HW vertex
                    behavior[iFormat] = D3DCREATE_HARDWARE_VERTEXPROCESSING;
                    if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat],
                        currFormat)))
                    {
                        formatWorks[iFormat] = TRUE;
                    }
                }

                if (!formatWorks[iFormat])
                {
                    /// HW vertex didn't work--can we do mixed?
                    behavior[iFormat] = D3DCREATE_MIXED_VERTEXPROCESSING;

                    if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat],
                        currFormat)))
                    {
                        formatWorks[iFormat] = TRUE;
                    }
                }
            }

            if (!formatWorks[iFormat])
            {
                /// Egads. Try SW vertex processing
                behavior[iFormat] = D3DCREATE_SOFTWARE_VERTEXPROCESSING;

                if (SUCCEEDED(IConfirmDevice(&deviceInfo->fDDCaps, behavior[iFormat],
                    currFormat)))
                {
                    formatWorks[iFormat] = TRUE;
                }
            }

            if (formatWorks[iFormat])
            {
                /// Now go through all the modes. If a given mode has a format that works,
                /// add it to the device
                UINT numAdapterModes = pD3D->GetAdapterModeCount(iAdapter, currFormat);
                DWORD iMode;
                for (iMode = 0; iMode < numAdapterModes; iMode++)
                {
                    // TODO: Check for modes that only differ by refresh rate and exclude duplicates.

                    /// Get the mode attributes
                    D3DDISPLAYMODE      dispMode;
                    pD3D->EnumAdapterModes(iAdapter, currFormat, iMode, &dispMode);
                    {
                        /// Add it to our driver's global mode list
                        D3DEnum_ModeInfo *modeInfo = drivInfo->fModes.Push();
                        ZeroMemory( modeInfo, sizeof( *modeInfo ) );
                        modeInfo->fDDmode = dispMode;
                        sprintf( modeInfo->fStrDesc, TEXT( "%ld x %ld x %ld" ), dispMode.Width, dispMode.Height, bitDepth );
                        modeInfo->fBitDepth = bitDepth;

                        // Add it to the device
                        modeInfo->fDDBehavior = behavior[ iFormat ];
                        IFindDepthFormats( pD3D, iAdapter, deviceInfo->fDDType, modeInfo );
                        IFindFSAATypes( pD3D, iAdapter, deviceInfo->fDDType, modeInfo );
                        ICheckCubicRenderTargets( pD3D, iAdapter, deviceInfo->fDDType, modeInfo );
                        deviceInfo->fModes.Append( *modeInfo );

                        // Special check for the desktop, which we know is the first entry, because we put it there.
                        if (iFormat == 0)
                        {
                            /// Check if the device can window and/or is compatible with the desktop display mode
                            deviceInfo->fCompatibleWithDesktop = TRUE;

                            // As of DirectX 9, any device supports windowed mode
                            //if (deviceInfo->fDDCaps.Caps2 & D3DCAPS2_CANRENDERWINDOWED)
                            {
                                deviceInfo->fCanWindow = TRUE;

                                /// Add a fake mode to represent windowed. Silly, but here for legacy
                                D3DEnum_ModeInfo *pModeInfo = drivInfo->fModes.Push();
                                ZeroMemory(pModeInfo, sizeof(*pModeInfo));
                                pModeInfo->fDDmode = dispMode;
                                pModeInfo->fDDBehavior = behavior[iFormat];
                                pModeInfo->fBitDepth = bitDepth;
                                sprintf(pModeInfo->fStrDesc, TEXT("Windowed"));
                                pModeInfo->fWindowed = true;

                                IFindDepthFormats(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo);
                                IFindFSAATypes(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo);
                                ICheckCubicRenderTargets(pD3D, iAdapter, deviceInfo->fDDType, pModeInfo);
                                deviceInfo->fModes.Append( *pModeInfo );
                            }
                        }
                    }
                }


            }
        }
    }

    delete [] formatWorks;
    delete [] behavior;
}

//// IFindDepthFormats ////////////////////////////////////////////////////////
//  DirectX: Given a device and mode, find ALL available depth/stencil
//  formats and add them to the mode info struct.

hsBool  hsGDirect3DTnLEnumerate::IFindDepthFormats( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType,
                                                   D3DEnum_ModeInfo *modeInfo )
{
    D3DFORMAT       formats[] = { D3DFMT_D16, D3DFMT_D24X8, D3DFMT_D32,
        D3DFMT_D15S1, D3DFMT_D24X4S4, D3DFMT_D24S8, D3DFMT_UNKNOWN };

    /// Try 'em
    for( int i = 0; formats[ i ] != D3DFMT_UNKNOWN; i++ )
    {
        if( SUCCEEDED( pD3D->CheckDeviceFormat( iAdapter, deviceType, modeInfo->fDDmode.Format,
            D3DUSAGE_DEPTHSTENCIL, D3DRTYPE_SURFACE, 
            formats[ i ] ) ) )
        {
            if( SUCCEEDED( pD3D->CheckDepthStencilMatch( iAdapter, deviceType, 
                modeInfo->fDDmode.Format, modeInfo->fDDmode.Format, formats[ i ] ) ) )
            {
                modeInfo->fDepthFormats.Append( formats[ i ] );
            }
        }
    }

    return( modeInfo->fDepthFormats.GetCount() > 0 ? true : false );
}

//// IFindFSAATypes ///////////////////////////////////////////////////////////
//  DirectX: Given a device and mode, find ALL available multisample types
//  and add them to the mode info struct.

hsBool  hsGDirect3DTnLEnumerate::IFindFSAATypes( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType,
                                                D3DEnum_ModeInfo *modeInfo )
{
    /// Try 'em
    for (int type = 2; type <= 16; type++)
    {
        if (SUCCEEDED(pD3D->CheckDeviceMultiSampleType(iAdapter, deviceType, modeInfo->fDDmode.Format, 
            modeInfo->fWindowed ? TRUE : FALSE,
            (D3DMULTISAMPLE_TYPE)type, NULL)))
        {
            modeInfo->fFSAATypes.Append((D3DMULTISAMPLE_TYPE)type);
        }
    }

    return (modeInfo->fFSAATypes.GetCount() > 0 ? true : false);
}

//// ICheckCubicRenderTargets /////////////////////////////////////////////////

hsBool  hsGDirect3DTnLEnumerate::ICheckCubicRenderTargets( IDirect3D9 *pD3D, UINT iAdapter, D3DDEVTYPE deviceType,
                                                          D3DEnum_ModeInfo *modeInfo )
{
    if( SUCCEEDED( pD3D->CheckDeviceFormat( iAdapter, deviceType, modeInfo->fDDmode.Format,
        D3DUSAGE_RENDERTARGET, D3DRTYPE_CUBETEXTURE, 
        modeInfo->fDDmode.Format ) ) )
    {
        modeInfo->fCanRenderToCubic = true;
        return true;
    }

    modeInfo->fCanRenderToCubic = false;
    return false;
}

//// IConfirmDevice ///////////////////////////////////////////////////////////
//
//  Nice, encapsulated way of testing for specific caps on a particular device

HRESULT hsGDirect3DTnLEnumerate::IConfirmDevice( D3DCAPS9* pCaps, DWORD dwBehavior,
                                                D3DFORMAT Format )
{
    short       bits;


    bits = IGetDXBitDepth( Format );
    if( bits == 16 || bits == 24 || bits == 32 )
        return S_OK;

    return E_FAIL;
}


//-----------------------------------------------------------------------------
// Name: ~hsGDirect3DTnLEnumerate()
// Desc: 
//-----------------------------------------------------------------------------
hsGDirect3DTnLEnumerate::~hsGDirect3DTnLEnumerate()
{
    D3DEnum_FreeResources();
}

//-----------------------------------------------------------------------------
// Name: D3DEnum_FreeResources()
// Desc: Frees all resources used for driver enumeration
//-----------------------------------------------------------------------------
VOID hsGDirect3DTnLEnumerate::D3DEnum_FreeResources()
{
}

//-----------------------------------------------------------------------------
// Name: SetEnumeErrorStr()
// Desc: 
//-----------------------------------------------------------------------------
void hsGDirect3DTnLEnumerate::SetEnumeErrorStr(const char* s) 
{ 
    hsStrncpy(fEnumeErrorStr, s, 128); 
}

//// IGetDXBitDepth //////////////////////////////////////////////////////////
//
//  From a D3DFORMAT enumeration, return the bit depth associated with it.
//  Copied from hsGDirect3DDevice to prevent inclusion of that class in
//  the RenderMenu project (for some reason, VC can't figure out we're only
//  calling one static function!)

short   hsGDirect3DTnLEnumerate::IGetDXBitDepth( D3DFORMAT format )
{
#define ReturnDepth(type, depth) if (format == type) return depth

    ReturnDepth(D3DFMT_UNKNOWN, 0);
    ReturnDepth(D3DFMT_A8R8G8B8, 32);
    ReturnDepth(D3DFMT_X8R8G8B8, 32);
    ReturnDepth(D3DFMT_R5G6B5, 16);
    ReturnDepth(D3DFMT_X1R5G5B5, 16);
    ReturnDepth(D3DFMT_A1R5G5B5, 16);

    // Supported by DX9, but we don't currently support it. Can add support if needed.
    //ReturnDepth(D3DFMT_A2B10G10R10, 32); 

    // Unsupported translation format--return 0
    return 0;
}

///////////////////////////////////////////////////////////////////////////////
//// Direct3D DeviceSelector Code ///////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

//// IGetD3DCardInfo /////////////////////////////////////////////////////////
//  Given two enum structs, strips out and produces the vendor ID, device ID
//  and the driver name. Returns true if processed, false otherwise.

hsBool  hsG3DDeviceSelector::IGetD3DCardInfo( hsG3DDeviceRecord &record,            // In
                                              void *driverInfo,
                                              void *deviceInfo,
                                              DWORD *vendorID, DWORD *deviceID, // Out
                                              char **driverString, char **descString  )
{
    D3DEnum_DriverInfo  *driverD3DInfo = (D3DEnum_DriverInfo *)driverInfo;
    D3DEnum_DeviceInfo  *deviceD3DInfo = (D3DEnum_DeviceInfo *)deviceInfo;

    D3DADAPTER_IDENTIFIER9  *adapterInfo;

    adapterInfo = &driverD3DInfo->fAdapterInfo;

    /// Print out to our demo data file
    plDemoDebugFile::Write( "DeviceSelector detected DX Direct3D device. Info:" );
    plDemoDebugFile::Write( "   Driver Description", (char *)adapterInfo->Description );
    plDemoDebugFile::Write( "   Driver Name", (char *)adapterInfo->Driver );
    plDemoDebugFile::Write( "   Vendor ID", (int32_t)adapterInfo->VendorId );
    plDemoDebugFile::Write( "   Device ID", (int32_t)adapterInfo->DeviceId );
    plDemoDebugFile::Write( "   Version", (char *)record.GetDriverVersion() );
    plDemoDebugFile::Write( "   Memory size (in MB)", record.GetMemoryBytes() / ( 1024 * 1024 ) );
    plDemoDebugFile::Write( "   Memory size (in bytes)", record.GetMemoryBytes() );

    *vendorID = adapterInfo->VendorId;
    *deviceID = adapterInfo->DeviceId;
    *driverString = adapterInfo->Driver;
    *descString = adapterInfo->Description;

    return true;
}

//// IInitDirect3D ////////////////////////////////////////////////////////////

hsBool  hsG3DDeviceSelector::IInitDirect3D( void )
{
    if( hsGDDrawDllLoad::GetD3DDll() == nil )
    {
        strcpy( fErrorString, "Cannot load Direct3D driver!" );
        return false;   
    }

    Direct3DCreateProc      procPtr;
    procPtr = (Direct3DCreateProc)GetProcAddress( hsGDDrawDllLoad::GetD3DDll(), "Direct3DCreate9" );
    if( procPtr == nil )
    {
        strcpy( fErrorString, "Cannot load D3D Create Proc!" );
        return false;
    }

    // Create a D3D object to use
    IDirect3D9      *pD3D = procPtr( D3D_SDK_VERSION );
    if( pD3D == nil )
    {
        strcpy( fErrorString, "Cannot load DirectX!" );
        return false;
    }
    pD3D->Release();

    fErrorString[ 0 ] = 0;
    return true;
}

//// ITryDirect3DTnL //////////////////////////////////////////////////////////

void hsG3DDeviceSelector::ITryDirect3DTnL(hsWinRef winRef)
{
    hsGDirect3DTnLEnumerate d3dEnum;

    int i;
    for( i = 0; i < d3dEnum.GetNumDrivers(); i++ )
    {
        ITryDirect3DTnLDriver(d3dEnum.GetDriver(i));
    }
}

//// ITryDirect3DDriver ///////////////////////////////////////////////////////
//
//  New DirectX Way

void hsG3DDeviceSelector::ITryDirect3DTnLDriver(D3DEnum_DriverInfo* drivInfo)
{
    hsG3DDeviceRecord devRec;
    devRec.Clear();
    devRec.SetG3DDeviceType( kDevTypeDirect3DTnL );

    devRec.SetDriverName( drivInfo->fAdapterInfo.Driver );
    devRec.SetDriverDesc( drivInfo->fAdapterInfo.Description );

    char    buff[ 256 ];
    sprintf( buff, "%d.%02d.%02d.%04d",
        HIWORD( drivInfo->fAdapterInfo.DriverVersion.u.HighPart ),
        LOWORD( drivInfo->fAdapterInfo.DriverVersion.u.HighPart ),
        HIWORD( drivInfo->fAdapterInfo.DriverVersion.u.LowPart ),
        LOWORD( drivInfo->fAdapterInfo.DriverVersion.u.LowPart ) );


    devRec.SetDriverVersion(buff);

    devRec.SetMemoryBytes(drivInfo->fMemory);

    int i;
    for( i = 0; i < drivInfo->fDevices.GetCount(); i++ )
    {
        /// 9.6.2000 mcn - Changed here so we can do fudging here, rather
        /// than passing all the messy driver data to the function
        hsG3DDeviceRecord   currDevRec = devRec;

        /// Done first now, so we can alter the D3D type later
        ITryDirect3DTnLDevice( &drivInfo->fDevices[i], currDevRec );

        /// Check the vendor ID to see if it's 3dfx (#0x121a). If it is, don't add it.
        /// (we don't support 3dfx D3D devices) -mcn
        /// 11.25.2000 mcn - Knew this was going to come back and bite me. Now we just
        /// append (3dfx) to the end of the device description, so that our latter test
        /// can throw it out or not, depending on whether we're "strong".

        if( drivInfo->fAdapterInfo.VendorId == 0x121a && 
            ( currDevRec.GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DHALDev ||
            currDevRec.GetG3DHALorHEL() == hsG3DDeviceSelector::kHHD3DTnLHalDev ) )
        {   
            if( drivInfo->fAdapterInfo.DeviceId >= 0x00000009 )
            {
                currDevRec.SetG3DHALorHEL( kHHD3D3dfxVoodoo5Dev );
                plDemoDebugFile::Write( "  Tagging device as a 3dfx Voodoo5 or above" );
            }
            else
            {
                currDevRec.SetG3DHALorHEL( kHHD3D3dfxDev );
                plDemoDebugFile::Write( "  Tagging device as a non-V5 3dfx card" );
            }
        }

        IFudgeDirectXDevice( currDevRec, (D3DEnum_DriverInfo *)drivInfo, (D3DEnum_DeviceInfo *)&drivInfo->fDevices[ i ] );

        if( currDevRec.GetModes().GetCount() )
            fRecords.Append( currDevRec );
    }
}

//// ITryDirect3DTnLDevice ////////////////////////////////////////////////////
//
//  New DirectX Way

void hsG3DDeviceSelector::ITryDirect3DTnLDevice(D3DEnum_DeviceInfo* devInfo, hsG3DDeviceRecord& devRec)
{
    devRec.SetDeviceDesc(devInfo->fStrName);

    if( devInfo->fDDType == D3DDEVTYPE_REF )
        devRec.SetG3DHALorHEL( kHHD3DRefDev );
    else if( devInfo->fDDType == D3DDEVTYPE_HAL )
    {
        if( devInfo->fDDCaps.DevCaps & D3DDEVCAPS_HWTRANSFORMANDLIGHT )
        {   
            devRec.SetG3DHALorHEL( kHHD3DTnLHalDev );
            devRec.SetCap( kCapsHWTransform );
        }
        else
            devRec.SetG3DHALorHEL( kHHD3DHALDev );
    }

    if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_CUBEMAP )
        devRec.SetCap( kCapsCubicTextures );

    devRec.SetLayersAtOnce( devInfo->fDDCaps.MaxSimultaneousTextures );

    if( devInfo->fDDCaps.TextureFilterCaps & D3DPTFILTERCAPS_MIPFLINEAR )
        devRec.SetCap( kCapsMipmap );
    if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_MIPCUBEMAP )
        devRec.SetCap( kCapsCubicMipmap );
    if( devInfo->fDDCaps.TextureCaps & D3DPTEXTURECAPS_PERSPECTIVE )
        devRec.SetCap(kCapsPerspective);
    if( devInfo->fIsHardware )
        devRec.SetCap( kCapsHardware );
    if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_DITHER )
        devRec.SetCap(kCapsDither);
    if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_WBUFFER )
        devRec.SetCap(kCapsWBuffer);
    if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGTABLE )
    {
        devRec.SetCap( kCapsFogLinear );
        devRec.SetCap( kCapsFogExp );
        devRec.SetCap( kCapsFogExp2 );
        devRec.SetCap( kCapsPixelFog );
    }
    else
    {
        devRec.SetCap( kCapsFogLinear );
    }
    if( devInfo->fDDCaps.RasterCaps & D3DPRASTERCAPS_FOGRANGE )
        devRec.SetCap( kCapsFogRange );

    if( devInfo->fDDCaps.MaxAnisotropy <= 1 )
        devRec.SetMaxAnisotropicSamples( 0 );
    else
        devRec.SetMaxAnisotropicSamples( (uint8_t)devInfo->fDDCaps.MaxAnisotropy );

    if (D3DSHADER_VERSION_MAJOR(devInfo->fDDCaps.PixelShaderVersion) > 0)
        devRec.SetCap(kCapsPixelShader);

    /// Assume these by default
    devRec.SetCap( kCapsCompressTextures );
    devRec.SetCap( kCapsDoesSmallTextures );

#if 1 // mf - want to leave this one off by default
    //  if( devInfo->fCanAntialias )
    //      devRec.SetCap( kCapsAntiAlias );
#endif // mf - want to leave this one off by default

    hsG3DDeviceMode devMode;
    int i, j;

    const struct 
    {
        D3DFORMAT fmt; uint16_t depth; 
    } depths[] = { { D3DFMT_D16, 0x0010 }, { D3DFMT_D24X8, 0x0018 }, { D3DFMT_D32, 0x0020 },
    { D3DFMT_D15S1, 0x010f }, { D3DFMT_D24X4S4, 0x0418 }, { D3DFMT_D24S8, 0x0818 }, { D3DFMT_UNKNOWN, 0 } };

    for( i = 0; i < devInfo->fModes.GetCount(); i++ )
    {
        D3DEnum_ModeInfo* modeInfo = &devInfo->fModes[i];

        devMode.Clear();
        devMode.SetWidth( modeInfo->fDDmode.Width );
        devMode.SetHeight( modeInfo->fDDmode.Height );
        devMode.SetColorDepth( modeInfo->fBitDepth );

        if( modeInfo->fCanRenderToCubic )
            devMode.SetCanRenderToCubics( true );
        else
            devMode.SetCanRenderToCubics( false );

        for( j = 0; depths[ j ].depth != 0; j++ )
        {
            if( modeInfo->fDepthFormats.Find( depths[ j ].fmt ) != modeInfo->fDepthFormats.kMissingIndex )
                devMode.AddZStencilDepth( depths[ j ].depth );
        }

        for( j = 2; j <= 16; j++ )
        {
            if( modeInfo->fFSAATypes.Find( (D3DMULTISAMPLE_TYPE)j ) != modeInfo->fFSAATypes.kMissingIndex )
                devMode.AddFSAAType( j );
        }

        devRec.GetModes().Append( devMode );
    }
}


