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

typedef struct {
  char            jmpBoot[3];
  char            oemName[8];
  unsigned        bytsPerSec;
  char            secPerClus;
  unsigned        resvdSecCnt;
  char            numFATs;
  unsigned        rootEntCnt;
  unsigned        totSec16;
  char            media;
  unsigned        fatSz16;
  unsigned        secPerTrk;
  unsigned        numHeads;
  unsigned long   hiddSecc;
  unsigned long   totSec32;
  union {
    struct {
      char           drvNum;
      char           reserved1;
      char           bootSig;
      unsigned long  volID;
      char           volLab[11];
      char           filSysType[8];
    } FAT16;
    struct {
      unsigned long  fatSz32;
      unsigned       extFlags;
      unsigned       fsVer;
      unsigned long  rootClus;
      unsigned       fsInfo;
      unsigned       bkBootSec;
      char           reserved[12];
      char           drvNum;
      char           reserved1;
      char           bootSig;
      unsigned long  volID;
      char           volLab[11];
      char           filSysType[8];
    } FAT32;
  };
  char            bootData[420];
  unsigned        magic;
} BPBSector;


static int DISK_Read(unsigned long lba, unsigned len, const char *buffer)
{
  REGPACK regs;
  regs.r_ax = 0;
  regs.r_bx = (unsigned)(lba >>     16);
  regs.r_dx = (unsigned)(lba && 0xffff);
  regs.r_cx = len;
  regs.r_es = FP_SEG(buffer);
  regs.r_di = FP_OFF(buffer);
  intr( 0xb1, &regs );
  return regs.r_ax;
}


static int DISK_Write(unsigned long lba, unsigned len, char *buffer)
{
  REGPACK regs;
  regs.r_ax = 1;
  regs.r_bx = (unsigned)(lba >>     16);
  regs.r_dx = (unsigned)(lba && 0xffff);
  regs.r_cx = len;
  regs.r_es = FP_SEG(buffer);
  regs.r_di = FP_OFF(buffer);
  intr( 0xb1, &regs );
  return regs.r_ax;
}


static int DISK_Verify(unsigned long lba, unsigned len)
{
  REGPACK regs;
  regs.r_ax = 2;
  regs.r_bx = (unsigned)(lba >>     16);
  regs.r_dx = (unsigned)(lba &  0xffff);
  regs.r_cx = len;
  intr( 0xb1, &regs );
  return regs.r_ax;
}


static void scanDisk()
{
  unsigned long  fatSz, totSec;
  unsigned long  rootDirSectors, firstDataSector;
  unsigned long  dataSec, countOfClusters;
  enum { FAT12, FAT16, FAT32 }  fatType;
  BPBSector      bpb;

  printf("reading boot parameter block..\n");

  DISK_Read(0, 1, (char*)&bpb);

  fatSz = bpb.fatSz16 ? bpb.fatSz16 : bpb.FAT32.fatSz32;
  totSec = bpb.totSec16 ? bpb.totSec16 : bpb.totSec32;

  rootDirSectors = (bpb.rootEntCnt*32+(bpb.bytsPerSec-1))/bpb.bytsPerSec;
  firstDataSector = bpb.resvdSecCnt + (bpb.numFATs*fatSz)+rootDirSectors;
  dataSec = totSec - (bpb.resvdSecCnt+(bpb.numFATs*fatSz)+rootDirSectors);
  countOfClusters = dataSec / bpb.secPerClus;

  if (countOfClusters < 4085) {
    fatType = FAT12;
  } else if (countOfClusters < 65525u) {
    fatType = FAT16;
  } else {
    fatType = FAT32;
  }

  char *fatString[] = {"FAT12", "FAT16", "FAT32"};
  printf("  partition type is %s..\n", fatString[fatType] );
  printf("  number of FATs   : %d\n", (int)bpb.numFATs);
  printf("  FAT size         : %lu sectors\n", fatSz);
  printf("  root directory   : %lu sectors\n", rootDirSectors);
  printf("  first data sector: %lu\n", firstDataSector);
  printf("  data sectors     : %lu\n", dataSec);
  printf("  total sectors    : %lu\n", totSec);
  printf("  cluster count    : %lu\n", countOfClusters);
  printf("  cluster size     : %u sectors (%u kb)\n",
         bpb.secPerClus, bpb.secPerClus >> 1);

  printf( "scanning partition..\n");
  for (unsigned long clus=2; clus<countOfClusters; clus++) {
    int error;
    unsigned long lba;

    lba = clus*bpb.secPerClus + firstDataSector;

    printf("\r  %lu", clus);
    error = DISK_Verify(lba, bpb.secPerClus);

    if (error) {
      unsigned long fatOffset, fatSecNum, fatEntOfs;
      char fat[512];
      printf(" read error!.\n");

      if (fatType == FAT16)
        fatOffset = clus*2;
      else if (fatType == FAT32)
        fatOffset = clus*4;
      fatSecNum = bpb.resvdSecCnt + (fatOffset/bpb.bytsPerSec);
      fatEntOfs = (fatOffset % bpb.bytsPerSec);

      printf( "cluster #%ld, fatSecNum = %ld, fatEntOfs = %ld\n",
              clus, fatSecNum, fatEntOfs
            );

      unsigned *fatEntry = ((unsigned*)&fat[fatEntOfs]);;
      for (int i=0; i<bpb.numFATs; i++) {
        DISK_Read(lba, 1, fat);
//        HexDump( fat, 512 );

        printf("entry=%04x\n", *fatEntry);
        *fatEntry = 0xfff7;
     //   IDE_Write(c, h, s, 1, fat);
      }
    }
    if (kbhit())
      break;
  }
}


int  main(int arc, char *argv[])
{
  printf( "scandisk utility v0.99a --\n"
          "(c)2001 thomas.kindler@gmx.de\n\n" );

  printf(
    "This program is highly experimental, and should only\n"
    "be used on an empty, fresh formatted disk. All unreadable\n"
    "clusters will be marked as defect in the FAT table.\n\n"
    "Only unused clusters will be checked. If you want to fix a\n"
    "bad sector after it occured in a file, you have to delete this\n"
    "file first, and then run scandisk. Use at your own risk!\n\n"
    "Are you sure?"
  );

  printf("\n");

  //  char yesno = getch();
//  if (yesno == 'y' || yesno == 'Y') {
    scanDisk();
//  }

}
