struct D3DDevice;
struct DisplayMode;

struct DisplayDriver
{
   DisplayDriver* Next;

   GUID Guid;
   char* Description;

   D3DDevice* D3DDeviceList;
   DisplayMode* DisplayModeList;
};

struct D3DDevice
{
   D3DDevice* Next;

   GUID Guid;
   char* Name;
   BOOL IsHW;
};

struct DisplayMode
{
   DisplayMode* Next;

   int Width;
   int Height;
   int Depth;
};

DisplayDriver*   DisplayDriverList;

LPDIRECTDRAW7   pDirectDraw;
LPDIRECT3D7     pDirect3D;

HRESULT CALLBACK EnumDisplayDrivers( GUID FAR* pGuid, LPSTR DriverDescription, LPSTR DriverName, LPVOID Context, HMONITOR HMonitor );
HRESULT CALLBACK EnumDisplayModes( LPDDSURFACEDESC2 pDDSD, LPVOID Context );
HRESULT CALLBACK EnumDevices( LPSTR DeviceDescription, LPSTR DeviceName, LPD3DDEVICEDESC7 pD3DDD, LPVOID Context );
void ReleaseLists();

BOOL EnumerateDirectX( HWND hWnd )
{
   ReleaseLists();

   DirectDrawEnumerateEx(( LPDDENUMCALLBACKEX )EnumDisplayDrivers, NULL, DDENUM_ATTACHEDSECONDARYDEVICES  | DDENUM_DETACHEDSECONDARYDEVICES | DDENUM_NONDISPLAYDEVICES );

   for( DisplayDriver* TheDD = DisplayDriverList; TheDD; TheDD = TheDD->Next )
   {
      DirectDrawCreateEx( &( TheDD->Guid ), ( void** ) &pDirectDraw, IID_IDirectDraw7, NULL );

      pDirectDraw->SetCooperativeLevel( hWnd, DDSCL_NORMAL );

      pDirectDraw->EnumDisplayModes( 0, NULL, &( TheDD->DisplayModeList ), ( LPDDENUMMODESCALLBACK2 )EnumDisplayModes );

      pDirectDraw->QueryInterface( IID_IDirect3D7, ( void** ) &pDirect3D );

      pDirect3D->EnumDevices(( LPD3DENUMDEVICESCALLBACK7 )EnumDevices, &( TheDD->D3DDeviceList ));

      DXRELEASE( pDirect3D );
      DXRELEASE( pDirectDraw );
   }

   return TRUE;
}

HRESULT CALLBACK EnumDisplayDrivers( GUID FAR* pGuid, LPSTR DriverDescription, LPSTR DriverName, LPVOID Context, HMONITOR HMonitor )
{
   DisplayDriver* NewDD = new DisplayDriver;
   if( NewDD )
   {
      ZeroMemory( NewDD, sizeof( DisplayDriver ));

      if( pGuid )
         CopyMemory( &( NewDD->Guid ), pGuid, sizeof( GUID ));

      NewDD->Description = new char[strlen( DriverDescription ) + 1];
      if( NewDD->Description )
      {
         strcpy( NewDD->Description, DriverDescription );
      }

      NewDD->DisplayModeList = NULL;
      NewDD->D3DDeviceList = NULL;
      NewDD->Next = NULL;
   }

   if( !DisplayDriverList )
      DisplayDriverList = NewDD;
   else
   {
      for( DisplayDriver* TheDD = DisplayDriverList; TheDD->Next; TheDD = TheDD->Next )
         ;

      TheDD->Next = NewDD;
   }
   
   return DDENUMRET_OK;
}

HRESULT CALLBACK EnumDisplayModes( LPDDSURFACEDESC2 pDDSD, LPVOID Context )
{
   DisplayMode** DMList = ( DisplayMode** )Context;

   DisplayMode* NewDM = new DisplayMode;
   if( NewDM )
   {
      NewDM->Width  = pDDSD->dwWidth;
      NewDM->Height = pDDSD->dwHeight;
      NewDM->Depth  = pDDSD->ddpfPixelFormat.dwRGBBitCount;

      NewDM->Next = NULL;
   }

   if( !( *DMList ))
      *DMList = NewDM;
   else
   {
      for( DisplayMode* TheDM = *DMList; TheDM->Next; TheDM = TheDM->Next )
         ;

      TheDM->Next = NewDM;
   }
   
   return DDENUMRET_OK;
}

HRESULT CALLBACK EnumDevices( LPSTR DeviceDescription, LPSTR DeviceName, LPD3DDEVICEDESC7 pD3DDD, LPVOID Context )
{
   D3DDevice** DevList = ( D3DDevice** )Context;

   D3DDevice* NewDev = new D3DDevice;
   if( NewDev )
   {
      CopyMemory( &( NewDev->Guid ), &( pD3DDD->deviceGUID ), sizeof( GUID ));

      NewDev->Name = new char[strlen( DeviceName ) + 1];
      if( NewDev->Name )
      {
         strcpy( NewDev->Name, DeviceName );
      }

      NewDev->IsHW = pD3DDD->dwDevCaps & D3DDEVCAPS_HWRASTERIZATION;
      NewDev->Next = NULL;
   }

   if( !( *DevList ))
      *DevList = NewDev;
   else
   {
      for( D3DDevice* TheDev = *DevList; TheDev->Next; TheDev = TheDev->Next )
         ;

      TheDev->Next = NewDev;
   }
   
   return DDENUMRET_OK;
}

void ReleaseLists()
{
   if( DisplayDriverList )
   {
      DisplayDriver* TheDD, * NextDD;

      for( TheDD = DisplayDriverList; TheDD; TheDD = NextDD )
      {
         NextDD = TheDD->Next;

         if( TheDD->Description )
            delete TheDD->Description;

         if( TheDD->DisplayModeList )
         {
            DisplayMode* TheDM, * NextDM;

            for( TheDM = TheDD->DisplayModeList; TheDM; TheDM = NextDM )
            {
               NextDM = TheDM->Next;

               delete TheDM;
            }
         }

         if( TheDD->D3DDeviceList )
         {
            D3DDevice* TheDev, * NextDev;

            for( TheDev = TheDD->D3DDeviceList; TheDev; TheDev = NextDev )
            {
               NextDev = TheDev->Next;

               if( TheDev->Name )
                  delete TheDev->Name;

               delete TheDev;
            }
         }

         delete TheDD;
      }

      DisplayDriverList = NULL;
   }
}