// =====================================================
// Прсмотр различной информации об устройствах
// чтения CD-ROM и компакт-дисках 
// с помощью интерфейса MSCDEX и обращением к драйверу
// устройства чтения CD-ROM
//
// (C) Фролов А.В, 1997
//
// E-mail: frolov@glas.apc.org
// WWW:    http://www.glasnet.ru/~frolov
//            или
//         http://www.dials.ccas.ru/frolov
// =====================================================
#include <stdio.h>
#include <stdlib.h>
#include <conio.h>
#include <dos.h>
#include <memory.h>
  
typedef unsigned char BYTE;
typedef unsigned int  WORD;
typedef unsigned long DWORD;

// Необходимо для обеспечения выравнивания 
// полей структур на границу байта
#pragma pack(1)

// Заголовок запроса для обращения к драйверу
typedef struct _ReqHdr
{
  BYTE bSize;
  BYTE bSubUnit;
  BYTE bCmd;
  WORD wStatus;
  BYTE bReserved[8];    
} ReqHdr;
  
// Запрос IOCTL Input
typedef struct _IOCTL_Input
{             
  ReqHdr rh;
  BYTE   bMediaDescriptor;
  DWORD  lpTransferAddress;
  WORD   wDataSize;
  WORD   wStartSector;
  DWORD  lpVolID;
} IOCTL_Input;       

// Запрос на получение информации о компакт-диске
typedef struct _DiskInfo
{
  BYTE bControl;
  BYTE bLowest;
  BYTE bHighest;
  DWORD dwTotal;
} DiskInfo;

// Запрос на получение информации 
// о дорожке компакт-диска
typedef struct _TrackInfo
{
  BYTE  bControl;
  BYTE  bTrack;
  DWORD dwLoc;
  BYTE  bInfo;
} TrackInfo;

// Запрос для определения текущего состояния
// устройства чтения CD-ROM
typedef struct _DeviceStatus
{
  BYTE bControl;
  DWORD dwParam;
} DeviceStatus;

// Запрос для определения общего количества
// секторов на компакт-диске
typedef struct _VolumeSize
{
  BYTE bControl;
  DWORD dwVolumeSize;
} VolumeSize;

#pragma pack()

// Прототипы функций
void GetCDLetters(BYTE *bLetters);
void CallCDDriver(void *rh, int nCDUnit);
int GetDiskInfo(int nCDUnit);
int GetDeviceStatus(int nCDUnit);
int GetVolumeSize(int nCDUnit);
int GetTrackInfo(int iTrack, int nCDUnit);
void delay(int ms);

// Регистры для вызова функции int86
union REGS rg;                      

// Количество установленных устройств чтения CD-ROM
int nCDUnits;

// Номер первого устройства чтения CD-ROM
int nCDStartUnit;

// Слово состояния после вызова драйвера CD-ROM
int iStatus;

// Информация о компакт-диске
DiskInfo     di;             

// Состояние устройства чтения CD-ROM
DeviceStatus ds;                     

// Объем компакт-диска
VolumeSize   vs;      

// Информация о дорожке
TrackInfo    ti;

BYTE bLetters[26];

// ---------------------------------------------------
// main
// Точка входа в программу
// ---------------------------------------------------
int main() 
{
  int iRetry;
  int i, j;
  
  printf("CDINFO, (c) A. Frolov, 1997\n\n");
  
  // Проверяем, установлена ли программа MSCDEX
  rg.x.ax = 0x1500;
  rg.x.bx = 0;
  int86(0x2f, &rg, &rg);

  if(rg.x.bx == 0)
  {
    printf("MSCDEX is not installed\n");
    return -1;
  }
  
  else        
  {
    // Сохраняем общее количество устройств чтения CD-ROM
    nCDUnits = rg.x.bx;
    
    // Сохраняем номер первого такого устройства
    nCDStartUnit = rg.x.cx;

    // Определяем и отображаем вресию MSCDEX
    rg.x.ax = 0x150c;
    int86(0x2f, &rg, &rg);
    printf("MSCDEX version: %d.%d\n", rg.h.bh, rg.h.bl);

    // Отображаем количество найденных устройств чтения
    // CD-ROM и номер первого устройства
    printf("Found %d CD Unit, start unit: %c\n",
      nCDUnits, nCDStartUnit + 'A');
  }

  // Получаем массив номеров устройств чтения CD-ROM
  GetCDLetters(bLetters);
  
  // Отображаем обозначения всех устройств CD-ROM
  printf("CD-ROM letters: ");
  for(i = 0; i < nCDUnits; i++)
  {
    printf("%c ", bLetters[i] + 'A');  
  }
  printf("\n");

  // Цикл по всем устройствам чтения CD-ROM
  for(i = 0; i < nCDUnits; i++)
  {
    // Определяем и отображаем состояние устройства
    iStatus = GetDeviceStatus(bLetters[i]);
    if(iStatus != 0x0100)
    {
      printf("GetDeviceStatus status: %04.4X\n", iStatus);
      printf("GetDeviceStatus failed\n");
      exit(1);               
    }
    printf("\nStatus of CD Drive %c: %08.8X\n", 
      bLetters[i] + 'A', ds.dwParam);

    // Определяем и отображаем объем устройства
    iStatus = GetVolumeSize(bLetters[i]);
    if(iStatus != 0x0100)
    {
      printf("GetVolumeSize status: %04.4X\n", iStatus);
      printf("GetVolumeSize failed\n");
    }
    else
      printf("VolumeSize: %ld blocks\n", vs.dwVolumeSize);

    // Определяем и отображаем информацию о
    // компакт-диске
    iStatus = GetDiskInfo(bLetters[i]);
    
    // Делаем три попытки получения информации
    iRetry = 0;
    while(iStatus != 0x0100)
    {
      printf("GetDiskInfo status: %04.4X\n", iStatus);
    
      // Задержка длительностью 1 с
      delay(1000);
    
      iRetry++;
    
      if(iRetry == 3)
      {
        printf("GetDiskInfo failed\n");
        break;
      }

      iStatus = GetDiskInfo(bLetters[i]);
    }
  
    // Если удалось получить информацию о компакт-диске,
    // исследуем его дорожки
    if(iRetry != 3)
    {
      // Выводим номера дорожек
      printf("Tracks: (%d - %d) %d\n", 
        di.bLowest, di.bHighest, di.dwTotal);

      // Цикл по всем дорожкам диска
      for(j = di.bLowest; j <= di.bHighest; j++)
      {
        // Получаем информацию о дорожке и отображаем ее
        GetTrackInfo(j, bLetters[i]);
        printf("track %d: location: %ld, info: %02.2X",
          j, ti.dwLoc, ti.bInfo);
      
        // Определяем тип дорожки - звуковая дорожка
        // или дорожка с данными
        if(ti.bInfo & 0x40)
          printf(" * digital");
        else
          printf(" * audio");

        // Определяем, разрашено ли копирование дорожки
        if(ti.bInfo & 0x20)
          printf(", copy permitted *\n");
        else
          printf(", copy prohibited *\n");
      }
    }  
  }
  return 0;
}

// ---------------------------------------------------
// GetDiskInfo
// Получение информации о компакт-диске
// ---------------------------------------------------
int GetDiskInfo(int nCDUnit)
{
  // Заголовок команды IOCTL Input
  IOCTL_Input cmd;
  
  // Очищаем заголовок
  memset(&cmd, 0, sizeof(IOCTL_Input ));
  
  // Заполняем заголовок
  cmd.rh.bSize    = 26;
  cmd.rh.bSubUnit = 0;
  cmd.rh.bCmd     = 3;
  
  cmd.bMediaDescriptor  = 0;
  cmd.lpTransferAddress = (DWORD)(void far *)&di;
  cmd.wDataSize         = 7;
  cmd.wStartSector      = 0;
  cmd.lpVolID           = (DWORD)(void far *)NULL;
  
  di.bControl = 10;
  
  // Вызываем драйвер
  CallCDDriver(&cmd, nCDUnit);
  
  return cmd.rh.wStatus;
}  

// ---------------------------------------------------
// GetTrackInfo
// Получение информации о дорожке компакт-диска
// ---------------------------------------------------
int GetTrackInfo(int iTrack, int nCDUnit)
{
  IOCTL_Input cmd;
  
  memset(&cmd, 0, sizeof(IOCTL_Input ));
  
  cmd.rh.bSize    = 26;
  cmd.rh.bSubUnit = 0;
  cmd.rh.bCmd     = 3;
  
  cmd.bMediaDescriptor  = 0;
  cmd.lpTransferAddress = (DWORD)(void far *)&ti;
  cmd.wDataSize         = 7;
  cmd.wStartSector      = 0;
  cmd.lpVolID           = (DWORD)(void far *)NULL;
  
  ti.bControl = 11;
  ti.bTrack = iTrack;
  
  CallCDDriver(&cmd, nCDUnit);
  
  return cmd.rh.wStatus;
}  

// ---------------------------------------------------
// GetDeviceStatus
// Определение состояния устройства чтения CD-ROM
// ---------------------------------------------------
int GetDeviceStatus(int nCDUnit)
{
  IOCTL_Input cmd;
  
  memset(&cmd, 0, sizeof(IOCTL_Input ));
  
  cmd.rh.bSize    = 26;
  cmd.rh.bSubUnit = 0;
  cmd.rh.bCmd     = 3;
  
  cmd.bMediaDescriptor  = 0;
  cmd.lpTransferAddress = (DWORD)(void far *)&ds;
  cmd.wDataSize         = 5;
  cmd.wStartSector      = 0;
  cmd.lpVolID           = (DWORD)(void far *)NULL;
  
  ds.bControl = 6;
  
  CallCDDriver(&cmd, nCDUnit);
  return cmd.rh.wStatus;
}  

// ---------------------------------------------------
// GetVolumeSize
// Определение объема компакт-диска
// ---------------------------------------------------
int GetVolumeSize(int nCDUnit)
{
  IOCTL_Input cmd;
  
  memset(&cmd, 0, sizeof(IOCTL_Input ));
  
  cmd.rh.bSize    = 26;
  cmd.rh.bSubUnit = 0;
  cmd.rh.bCmd     = 3;
  
  cmd.bMediaDescriptor  = 0;
  cmd.lpTransferAddress = (DWORD)(void far *)&vs;
  cmd.wDataSize         = 5;
  cmd.wStartSector      = 0;
  cmd.lpVolID           = (DWORD)(void far *)NULL;
  
  vs.bControl = 8;
  
  CallCDDriver(&cmd, nCDUnit);
  return cmd.rh.wStatus;
}  

// ---------------------------------------------------
// CallCDDriver
// Вызов драйвера компакт-диска
// ---------------------------------------------------
void CallCDDriver(void *rh, int nCDUnit)
{
  static union REGS rg;
  static struct SREGS srg;
  
  segread(&srg);
  rg.x.ax = 0x1510;
  rg.x.cx = nCDUnit;
  rg.x.bx = FP_OFF(rh);  
  
  int86x(0x2f, &rg, &rg, &srg);
}

// ---------------------------------------------------
// GetCDLetters
// Заполнение массива номерами установленных 
// в системе устройств чтения компакт-диска
// ---------------------------------------------------
void GetCDLetters(BYTE *bLetters)
{
  static union REGS rg;
  static struct SREGS srg;
  
  segread(&srg);
  rg.x.ax = 0x150d;
  rg.x.bx = FP_OFF(bLetters);  
  
  int86x(0x2f, &rg, &rg, &srg);
}

// ---------------------------------------------------
// delay
// Формирование задержки по таймеру
// ---------------------------------------------------
void delay(int ms) 
{
  int ticks;    
  
  ticks = ms / 55;
  _asm 
  {
    push si

    mov  si, ticks
    mov  ah, 0
    int  1ah

    mov  bx, dx
    add  bx, si

delay_loop:

    int  1ah
    cmp  dx, bx
    jne  delay_loop

    pop  si
  }
}
