/*-
 * DiskChecker - check a floppy disk for errors
 *
 * DiskChecker 1.0 Copyright 1989 C. Harald Koch. All Rights Reserved.
 *
 *      DiskChecker is based on the original version of DiskSalv by Dave
 *      Haynie. The DiskIO code is originally his.
 *
 * DiskChecker is freely redistributable, provided no fee is charged etc.
 *
 *
 * SYNOPSIS:
 *      DiskChecker dfn:
 *
 * Will check the floppy in drive n for any errors at the track and sector level.
 *
 * Compiles under Aztec C 3.4 with 32 bit integers.
 *
 *
-*/

#include        <exec/types.h>
#include        <exec/exec.h>
#include        <devices/trackdisk.h>
#include        <libraries/dos.h>
#include        <libraries/dosextens.h>
#include        <ctype.h>
#include        <stdio.h>

#include        <functions.h>
#include        "devinfo.h"

extern  void *  CreateExtIO();

char            prog_version[] = "1.0";

char           *td_errors[] = {
     /* 20 TDERR_NotSpecified    */ "Undetermined Error",
     /* 21 TDERR_NoSecHdr        */ "Couldn't Find Sector Header",
     /* 22 TDERR_BadSecPreamble  */ "Error in Sector Preamble",
     /* 23 TDERR_BadSecID        */ "Error in Sector Identifier",
     /* 24 TDERR_BadHdrSum       */ "Bad Header Field Checksum",
     /* 25 TDERR_BadSecSum       */ "Bad Sector Data Field Checksum",
     /* 26 TDERR_TooFewSecs      */ "Incorrect Number of Sectors on Track",
     /* 27 TDERR_BadSecHdr       */ "Unable to Read Sector Header",
     /* 28 TDERR_WriteProt       */ "Disk is Write Protected",
     /* 29 TDERR_DiskChanged     */ "Disk Changed or not Present",
     /* 30 TDERR_SeekError       */ "Seek Error While Verifying Seek Position",
     /* 31 TDERR_NoMem           */ "Not Enough Memory",
     /* 32 TDERR_BadUnitNum      */ "Bad Unit Number",
     /* 33 TDERR_BadDriveType    */ "Bad Drive Type",
     /* 34 TDERR_DriveInUse      */ "Drive In Use",
     /* 35 TDERR_PostReset       */ "TDERR_PostReset"
};

#define LOWTDERR        20
#define HIGHTDERR       35

#define NUM_CYLS        (mdi->HighCyl - mdi->LowCyl + 1)
#define DISK_SIZE       (mdi->Surfaces*NUM_CYLS*mdi->BlocksPerTrack)
#define READOK          0

LONG            diskChangeCount = 0;    /* Checks for changed disk */
int             verbose = 0;    /* print every sector? */
int             summary = 1;    /* print every cylinder? */


static void
MotorOff(req)
struct IOExtTD *req;
{
    req->iotd_Req.io_Length = 0;
    req->iotd_Req.io_Command = TD_MOTOR;
    DoIO(req);
}


static void
InitDisk(req)
struct IOExtTD *req;
{
    req->iotd_Req.io_Command = TD_CHANGENUM;
    DoIO(req);
    diskChangeCount = req->iotd_Req.io_Actual;
}


BYTE
ReadBuf(req, buf, sect, len)
struct IOExtTD *req;
char           *buf;
ULONG           sect;
ULONG           len;
{
    req->iotd_Req.io_Length = len;
    req->iotd_Req.io_Data = (APTR) buf;
    req->iotd_Req.io_Command = ETD_READ;
    req->iotd_Count = diskChangeCount;
    req->iotd_Req.io_Offset = len * sect;
    DoIO(req);
    return (req->iotd_Req.io_Error);
}


int
checkDisk(mdi)
struct MyDevInfo *mdi;
{
    ULONG           sect, head, block, cylinder;        /* Current block */
    BYTE            result;
    struct IOExtTD *diskreq = NULL;
    struct MsgPort *diskport = NULL;
    char           *buf = NULL;

    if ((diskport = (struct MsgPort *) CreatePort("diskreq.port", 0)) == NULL) {
        printf("ERROR: Cannot create message port\n");
        goto error;
    }

    if ((diskreq = (struct IOExtTD *)
         CreateExtIO(diskport, sizeof(struct IOExtTD))) == NULL) {
        printf("ERROR: Cannot create I/O port\n");
        goto error;
    }

    if ((buf = (char *) AllocMem(mdi->BlockSize, mdi->BufMemType)) == NULL)
        goto error;

    if (OpenDevice(mdi->DeviceName, mdi->Unit, diskreq, 0)) {
        printf("ERROR: Cannot open TrackDisk device\n");
        goto error;
    }

    InitDisk(diskreq);

    sect = 0;
    printf("\2330 p");          /* turn cursor off for faster rendering */

    for (cylinder = mdi->LowCyl; cylinder <= mdi->HighCyl; cylinder++) {
        for (head = 0; head < mdi->Surfaces; head++) {
            for (block = 0; block < mdi->BlocksPerTrack; block++, sect++) {

                if ((result = ReadBuf(diskreq, buf, sect, mdi->BlockSize)) != READOK) {
                    printf(" Block %6d (C:%04d H:%d S:%02d) ", sect,
                           cylinder, head, block);
                    if (result >= LOWTDERR && result <= HIGHTDERR) {
                        printf(" Error %2d: %s\n", result, td_errors[result - LOWTDERR]);
                    }
                    else {
                        printf("Error %2d: Unknown Trackdisk Error\n", result);
                    }
                }
                else {
                    if (verbose || (summary && head == 0 && block == 0)) {
                        printf(" Block %6d (C:%04d H:%d S:%02d) ", sect,
                               cylinder, head, block);
                        printf("(OK)\233K\r");
                    }
                }
                fflush(stdout);

                if (checkbreak()) {
                    printf("\nBREAK detected!\n");
                    printf("\233 p"); /* turn cursor back on */
                    goto error;
                }
            }
        }
    }
    printf("\233 p\n");

error:
    if (buf != NULL)
        FreeMem(buf, mdi->BlockSize);
    if (diskreq != NULL) {
        MotorOff(diskreq);
        CloseDevice(diskreq);
        DeleteExtIO(diskreq, sizeof(struct IOExtTD));
    }
    if (diskport != NULL)
        DeletePort(diskport);

    return 0;
}


usage(s)
char           *s;
{
    fprintf(stderr, "usage: %s [-v|q] <dos-device-name>\n", s);
}

extern  int     getopt();
extern  int     optind;
extern  char *optarg;

int
main(argc, argv)
int             argc;
char           *argv[];
{
    char            stat;
    int             c;
    extern char    *optarg;
    char            name[20];
    int             len;
    struct MyDevInfo *mdi;

    disablebreak();

    printf("\nDiskChecker %s Copyright 1989 C. Harald Koch\n\n", prog_version);

    while((c = getopt(argc, argv, "vq")) != EOF) {
        switch(c) {
        case 'v':               /* run verbosely; print every sector */
            verbose = 1;
            break;
        case 'q':               /* run quitely; only print errors */
            verbose = summary = 0;
            break;
        default:
            usage(argv[0]);
            enablebreak();
            exit(20);
        }
    }

    if (optind != argc - 1) {
        usage(argv[0]);
        enablebreak;
        exit(10);
    }

    strcpy(name, argv[optind]);
    len = strlen(name);
    if (name[len - 1] == ':') {
        name[len - 1] = '\0';
        len--;
    }

    touppers(name);

    if ((mdi = getDevInfo(name)) != NULL) {
        printf("DEVICE     =    %s   (%s)\n", mdi->DeviceName, name);
        printf("UNIT       =    %5ld    FLAGS      =    %5ld\n", mdi->Unit, mdi->DeviceFlags);
        printf("HEADS      =    %5ld    SECTORS    =    %5ld\n", mdi->Surfaces, mdi->BlocksPerTrack);
        printf("LOCYL      =    %5ld    HICYL      =    %5ld\n", mdi->LowCyl, mdi->HighCyl);
        printf("LOBLOCK    =    %5ld    HIBLOCK    =    %5ld\n",
               mdi->LowCyl * mdi->Surfaces * mdi->BlocksPerTrack,
               (mdi->HighCyl + 1) * mdi->Surfaces * mdi->BlocksPerTrack - 1);
        printf("RESERVED   =    %5ld    MEMTYPE    =    %5ld\n", mdi->Reserved, mdi->BufMemType);
        printf("FILESYS    =    %5s    DISK SIZE  =    %5ld\n",
               (mdi->DosType == 0x444f5300) ? "OFS" : "FFS",
               (mdi->HighCyl - mdi->LowCyl + 1) * mdi->Surfaces * mdi->BlocksPerTrack);

        printf("\nShould I Continue? [y] ");
        if ((c = getchar()) != 'y' && c != 'Y' && c != '\n') {
            printf("Aborting...\n");
            exit(20);
        }

        stat = checkDisk(mdi);

        enablebreak();
        exit(stat);

    }
    else {
        printf("device not found (or device is not a disk device)\n");
        enablebreak();
        exit(20);
    }
}
