bool    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;
}
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 );
    }
}
void    hsG3DDeviceSelector::ISetFudgeFactors( uint8_t chipsetID, hsG3DDeviceRecord &record )
{
    int     i, maxIDs, j;


    maxIDs = sizeof( dsCFTable ) / sizeof( dsCFTable[ 0 ] );

    /// Search for our chipset
    for( i = 0; i < maxIDs; i++ )
    {
        if( dsCFTable[ i ].fType == chipsetID )
        {
            /// Found it!

            // Flags to force set
            if( dsCFTable[ i ].fFlagsToSet != nil )
            {
                for( j = 0; j < dsCFTable[ i ].fFlagsToSet[ 0 ]; j++ )
                    record.SetCap( dsCFTable[ i ].fFlagsToSet[ j + 1 ] );
            }

            // Flags to force clear
            if( dsCFTable[ i ].fFlagsToClear != nil )
            {
                for( j = 0; j < dsCFTable[ i ].fFlagsToClear[ 0 ]; j++ )
                    record.SetCap( dsCFTable[ i ].fFlagsToClear[ j + 1 ], false );
            }

            // Suckiness
            record.SetZBiasRating( dsCFTable[ i ].fZSuckiness );

            // Max # of layers
            if( dsCFTable[ i ].fForceMaxLayers > 0 )
                record.SetLayersAtOnce( dsCFTable[ i ].fForceMaxLayers );

            // LOD bias rating
            record.SetLODBiasRating( dsCFTable[ i ].fLODRating );

            // Fog tweaks
            FogTweakTable   *fogTweaks = dsCFTable[ i ].fFogTweaks;

            record.SetFogApproxStarts( fogTweaks->fFogExpApproxStart, fogTweaks->fFogExp2ApproxStart );
            record.SetFogEndBias( fogTweaks->fFogEndBias );
            record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp, fogTweaks->fFogExpKnee, fogTweaks->fFogExpKneeVal );
            record.SetFogKneeParams( hsG3DDeviceRecord::kFogExp2, fogTweaks->fFogExp2Knee, fogTweaks->fFogExp2KneeVal );

            if( record.GetCap(kCapsNoAA) )
            {
                int j;
                for( j = 0; j < record.GetModes().GetCount(); j++ )
                    record.GetModes()[j].ClearFSAATypes();
            }

            return;
        }
    }
}
void    hsG3DDeviceSelector::IFudgeDirectXDevice( hsG3DDeviceRecord &record,
                                                    D3DEnum_DriverInfo *driverInfo,
                                                    D3DEnum_DeviceInfo *deviceInfo )
{
    uint32_t    vendorID, deviceID;
    char        *szDriver, *szDesc;


    /// Send it off to each D3D device, respectively
    if( record.GetG3DDeviceType() == kDevTypeDirect3D )
    {
        if( !IGetD3DCardInfo( record, driverInfo, deviceInfo, &vendorID, &deviceID, &szDriver, &szDesc ) )
        {
            // {} to make VC6 happy in release build
            hsAssert( false, "Trying to fudge D3D device but D3D support isn't in this EXE!" );
        }
    }
    else
    {
        hsAssert( false, "IFudgeDirectXDevice got a device type that support wasn't compiled for!" );
    }

    /// So capitalization won't matter in our tests
    plString desc = plString::FromIso8859_1(szDesc).ToLower();

    /// Detect ATI Radeon chipset
    // We will probably need to differentiate between different Radeons at some point in 
    // the future, but not now.
    ssize_t radeon = desc.Find("radeon");
    if (stricmp(szDriver, "ati2dvag.dll") == 0 || radeon >= 0)
    {
        int series = 0;
        if (radeon >= 0)
        {
            const char* str = desc.c_str() + radeon + strlen("radeon");
            if( 1 == sscanf(str, "%d", &series) )
            {
                if( (series >= 8000) && (series < 9000) )
                {
                    hsStatusMessage( "== Using fudge factors for ATI Radeon 8X00 chipset ==\n" );
                    ISetFudgeFactors( kATIR8X00Chipset, record );
                }
                else if (series >= 9000)
                {
                    hsStatusMessage("== Using fudge factors for ATI Radeon 9X00 chipset ==\n");
                    ISetFudgeFactors(kATIRadeonChipset, record);
                }
                else
                {
                    series = 0;
                }
            }
        }
        if (series == 0)
        {
            hsStatusMessage("== Using fudge factors for ATI/AMD Radeon X/HD/R chipset ==\n");
            ISetFudgeFactors(kDefaultChipset, record);
        }
    }

    //// Other Cards //////////////////////////////////////////////////////////
    /// Detect Intel i810 chipset
    else if( deviceID == 0x00007125 &&
                ( stricmp( szDriver, "i81xdd.dll" ) == 0 
                  || ( desc.Find("intel") >= 0 && desc.Find("810") >= 0 ) ) )
    {
        hsStatusMessage( "== Using fudge factors for an Intel i810 chipset ==\n" );
        ISetFudgeFactors( kIntelI810Chipset, record );
    }
    /// Detect for a GeForc FX card. We only need to nerf the really low end one.
    else if( desc.Find("nvidia") >= 0 && desc.Find("geforce fx 5200") >= 0 )
    {
        hsStatusMessage( "== Using fudge factors for an NVidia GeForceFX-based chipset ==\n" );
        ISetFudgeFactors( kNVidiaGeForceFXChipset, record );
    }
    /// Default fudge values
    else
    {
        hsStatusMessage( "== Using default fudge factors ==\n" );
        ISetFudgeFactors( kDefaultChipset, record );
    }
}