#include <stdio.h>
#include <conio.h>
#include <malloc.h>
#include <dos.h>
#include <ctype.h>

typedef struct _EBPB_
{
  unsigned sectsize;
  char clustsize;
  unsigned ressecs;
  char fatcnt;
  unsigned rootsize;
  unsigned totsecs;
  char media;
  unsigned fatsize;
  unsigned seccnt;
  unsigned headcnt;
  unsigned hiddensec_low;
  unsigned hiddensec_hi;
  unsigned long drvsecs;
} EBPB;

typedef struct _BOOT_
{
  char jmp[3];
  char oem[8];
  EBPB bpb;
  char drive;
  char reserved;
  char signature;
  unsigned volser_lo;
  unsigned volser_hi;
  char label[11];
  char fat_format[8];
  char boot_code[450];
} BOOT;

typedef struct _FTIME_
{
  unsigned sec : 5, min : 6, hour : 5;
} FTIME;

typedef struct _FDATE_
{
  unsigned day : 5, month : 4, year : 7;
} FDATE;

typedef struct _FITEM_
{
  char name[8];
  char ext[3];
  char attr;
  char reserved[10];
  FTIME time;
  FDATE date;
  unsigned cluster_nu;
  unsigned long size;
} FITEM;

int getboot(BOOT far *boot, int drive);

union REGS reg;
struct SREGS segreg;

struct
{
  unsigned long first_sect;
  unsigned nsect;
  void far* buf;
} cb;

int main(void)
{
  int i;
  int  root_begin, root_sectors;
  char drive;

  char boot[512];
  BOOT far* boot_rec = (BOOT far*) boot;

  FITEM *root_buffer, far *rptr;

  printf("\nЧтение корневого каталога "
    "логического диска"
    "\n  (C)Фролов А., 1995\n");

  // Запрашиваем диск, для которого необходимо
  // выполнить чтение загрузочной записи.
  printf("\nВведите обозначение диска (A, B, ...):");
  drive = getche();

  // Вычисляем номер дисковода
  drive = toupper(drive) - 'A';

  // Читаем загрузочную запись в буфер
  int status = getboot((BOOT far*)boot_rec, drive);

  // Если произошла ошибка (например, неправильно указано
  // обозначение диска), завершааем работу программы
  if(status)
  {
    printf("\nОшибка при чтении загрузочного сектора");
    return(-1);
  }

  // Вычисляем номер первого сектора
  // корневого каталога
  root_begin = boot_rec->bpb.ressecs +
    boot_rec->bpb.fatsize * boot_rec->bpb.fatcnt;

  // Вычисляем длину корневого каталога
  root_sectors = (boot_rec->bpb.rootsize * 32) /
    boot_rec->bpb.sectsize;

  // Заказываем буфер для корневого каталога
  root_buffer = (FITEM *)
    malloc(root_sectors * boot_rec->bpb.sectsize);

  if(root_buffer == NULL)
  {
    printf("\nМало памяти");
    return(-1);
  }

  // Читаем корневой каталог в буфер root_buffer
  cb.first_sect = root_begin;
  cb.nsect = root_sectors;
  cb.buf = (void far*)root_buffer;

  _BX  = FP_OFF(&cb);
  _DS = FP_SEG(&cb);
  _CX  = 0xffff;
  _DX  = 0;
  _AX  = drive;
  asm int 25h
  asm pop ax
  asm jc error

  // Показываем содержимое корневого каталога
  printf("\n"
    "\nИмя файла    Аттр. Дата        "
    "Время     Кластер Размер"
    "\n------------ ----- ----------  "
    "--------  ------- ------");

  for(rptr = root_buffer;; rptr++)
  {
    printf("\n");

    // Признак конца каталога - нулевой байт в начале
    // имени файла или байт 0xF6 (пустой каталог)
    if(rptr->name[0] == 0 ||
       rptr->name[0] == (char)0xF6) break;

    // Выводим содержимое дескриптора файла
    for(i=0; i<8; i++)
      printf("%c",rptr->name[i]);

    printf(".");

    for(i=0; i<3; i++)
      printf("%c",rptr->ext[i]);

    printf(" %02X    %02d-%02d-%02d  %02d:%02d:%02d ",
      rptr->attr, rptr->date.day,
      rptr->date.month, rptr->date.year + 1980,
      rptr->time.hour, rptr->time.min,
      rptr->time.sec * 2);

    printf(" %-6ld   %lu",
      (long)rptr->cluster_nu, (long)rptr->size);
  }

  // Освобождаем память
  free(root_buffer);
  return 0;

error:
  printf("\nОшибка при чтении каталога");
  free(root_buffer);
  return(-1);
}

/**
* getboot
*
* Прочитать загрузочную запись
*
* int getboot(BOOT far *boot, int drive);
*
*  boot   - указатель на буфер, в который
*           будет прочитана загрузочная запись
*
*  drive  - номер физического НМД
*           (0 - первый НМД, 1 - второй, ...)
**/
int getboot(BOOT far *boot, int drive)
{
  cb.first_sect = 0;
  cb.nsect = 1;
  cb.buf = (void far*)boot;

  _BX  = FP_OFF(&cb);
  _DS = FP_SEG(&cb);
  _CX  = 0xffff;
  _DX  = 0;
  _AX  = drive;

  asm int 25h

  // Извлекаем из стека оставшееся там после
  // вызова прерывания слово
  asm pop ax
  asm jc err

  return(0);
err:
  return(1);
}

