/* fhdx.c */

/*
 * Atari Hard Disk Installation Utility (Factory Version)
 * Copyright 1988, 1989 Atari Corp.
 *
 * Associated files
 *	fhdx.rsc	resource file
 *	wincap		hard disk database (text file)
 *
 *	fhdx.h		object tree definitions
 *	define.h	constant definitions
 *	part.h		structure definitions
 *
 *	fhdx.c		top level, user interface (this file)
 *	fmt.c		disk formatting
 *	part.c		partition reading/writing
 *	sect.c		sector reading, writing, zeroing
 *	string.c	string functions (matching, concat, ...)
 *	wincap.c	hard disk parameter / partition size database
 *	st.c		random ST functions (delay, reboot, ...)
 *
 *----
 * 19-Jan-1988	ml.	Modified the user version to 'this'.
 * 14-Sep-1989	ml.	Modified to use Lrwabs() instead of hread()
 *			and hwrite().  Will handle both SCSI and
 *			ACSI drives.
 */

#include <obdefs.h>
#include <gemdefs.h>
#include <osbind.h>
#include "fhdx.h"
#include "define.h"
#include "part.h"
#include "bsl.h"
#include "deflst.h"
#include "addr.h"
#include "lrwabs.h"


/* Globals */
int rebootp = 0;	/* 1: must reboot (not return to desktop) */
int running;		/* 1: continue evnt_multi() loop */
int maxbadsects;	/* maximum number bad sectors allowed */
int scsixst;		/* 1: SCSI exists */
char cachexst;		/* 1: '030 cache exists */
char sbuf[512];		/* error message buffer */
char bs[512];		/* root sector image */

/* Default values */
int cdev = DEFUNIT;		/* current physical unit */
char cdmdl[16]=DEFDMDL;		/* current disk model */
char cdid[3]=DEFID;		/* id for current disk model */
char cdtype[5]=DEFDTYPE;	/* current disk type (ACSI or SCSI) */
long pattern = DEFPATTERN;	/* test pattern of current disk model */

int pdev;		/* physical unit # of curr physical unit */
int modesel;		/* flag for mode select */
HINFO hinfo;		/* physical parameters of curr disk model */

/* Translate unit number to tree index. */
int acsiphys[] = {
    UNIT0, UNIT1
};
int scsiphys[] = {
    UNIT8, UNIT9
};


static char *rscorrupt = "[3][Fatal Error!|Corrupt Resource File][EXIT]";
long ostack;		/* old stack value */
SECTOR *badbuf;		/* buffer storing bad sectors */

extern long gbslsiz(), nument();
extern char *bsl;
extern long bslsiz;

/* Partition related variables */
long maxpsiz = MAXPSIZ;	/* maximum partition size */

/* AES (windows and messages) related variables */
int gl_hchar;		/* height of system font (pixels) */
int gl_wchar;		/* width of system font (pixels) */
int gl_wbox;		/* width of box able to hold system font */
int gl_hbox;		/* height of box able to hold system font */

int phys_handle;	/* physical workstation handle */
int handle;		/* virtual workstation handle */
int wi_handle;		/* window handle */

int formw, formh, sx, sy, lx, ly;	/* dialogue box dimensions */
int xdesk, ydesk, hdesk, wdesk;		/* window X, Y, width, height */

int msgbuff[8];		/* event message buffer */
int keycode;		/* keycode returned by event-keyboard */
int mx, my;		/* mouse x and y pos. */
int butdown;		/* button state tested for, UP/DOWN */
int ret;		/* dummy return variable */

int hidden;		/* current state of cursor */

int contrl[12];
int intin[128];
int ptsin[128];
int intout[128];
int ptsout[128];	/* storage wasted for idiotic bindings */

int work_in[11];	/* Input to GSX parameter array */
int work_out[57];	/* Output from GSX parameter array */
int pxyarray[10];	/* input point array */


/*
 * Top level;
 * we get control from the desktop.
 */
main()
{
    long dummy;
    int ret, i;
    char *s, *wgetstr();
    extern int chkscsi();
    
    running = TRUE;
    appl_init();
    phys_handle=graf_handle(&gl_wchar, &gl_hchar, &gl_wbox, &gl_hbox);
    wind_get(0, WF_WORKXYWH, &xdesk, &ydesk, &wdesk, &hdesk);
    open_vwork();
    wi_handle=wind_create(WI_KIND, xdesk, ydesk, wdesk, hdesk);

    hidden = FALSE;
    butdown = TRUE;

    if (!rsrc_load(RESOURCEFILE)) {
	graf_mouse(ARROW, 0L);
	errs("[3][Fatal Error!|Resource file ",
	     RESOURCEFILE,
	     "|not found.][EXIT]");
	goto punt;
    }

    if (getalladdr() == ERROR) {
	corrupt();
	goto punt;
    }

    /* get screen resolution, if LOW, request user to change it to MEDIUM */
    if (!Getrez()) {
	err(midrez);
	goto punt;
    }

    /* Get default unit#, disk type, id and model from wincap if there is any */
    if (wgetent("Parameters", "@@") == OK) {
    	wgetnum("ms", &maxpsiz);		/* get max partition size */
    	if ((s = wgetstr("ty")) != NULL)	/* get disk type */
    	    strcpy(cdtype, s);
    	if (wgetnum("un", &dummy) == OK)	/* get unit # */
	    cdev = (WORD)dummy;
    	if ((s = wgetstr("id")) != NULL)	/* get disk id */
    	    strcpy(cdid, s);
	if ((s = wgetstr("ml")) != NULL)	/* get disk model */
	    strcpy(cdmdl, s);
    }
    
    /* Get parameters of current disk type and model */
    modesel = 1;
    if (gfparm(&modesel, &hinfo, cdmdl, cdid) != OK)
	return ERROR;
	
    /* Get test pattern for selected disk model */
    if (wgetnum("dp", &dummy) == OK)	/* if find one in wincap */
	pattern = dummy;		/*	use that one.	 */
    
    /* Close the wincap file after accessing it */
    wclose();
        	
    /* Calculate maximum number of bad sectors allowed */
    maxbadsects 
    	= (int)hinfo.hi_dhc * hinfo.hi_cc * (int)hinfo.hi_spt / MEGABYTE;

    /* Allocate memory for recording bad sectors */    	
    if ((badbuf = (SECTOR *)mymalloc(maxbadsects << 2)) <= 0L) {
    	ret = err(nomemory);
    	goto punt;
    }

    /* Check if cache exists */
    cachexst = (char)chkcache();
    
    /* Check if SCSI exists */
    if ((scsixst = chkscsi())) {
    	physdial[SCSIUNIT].ob_state = NORMAL;
    	for (i = 0; i < MAXPHYSDEV; i++)
    	    physdial[scsiphys[i]].ob_state = NORMAL;
    } else {
    	physdial[SCSIUNIT].ob_state = DISABLED;
    	for (i = 0; i < MAXPHYSDEV; i++)
    	    physdial[scsiphys[i]].ob_state = DISABLED;
    	strcpy(cdtype, "ACSI");		/* disk type must be ACSI */
    }
    
    /* default disk type is SCSI AND SCSI exists? */
    if (!strcmp(cdtype, "SCSI") && scsixst) {
	pdev = cdev + 8;		/* phys dev for SCSI */
	physdial[scsiphys[cdev]].ob_state = SELECTED;
    } else {
	pdev = cdev;			/* phys dev for ACSI */
    	physdial[acsiphys[cdev]].ob_state = SELECTED;
    }
    	
    /* display menu bar */
    menu_bar(menuobj, 1);
    
    /* display current unit and type on menu bar */
    unittype[0].ob_x = wdesk - unittype[0].ob_width;
    unittype[0].ob_y = ydesk - gl_hbox;
    dsply_utbox();
    
    graf_mouse(ARROW, 0L);
    while (running)
    	domulti();

    /*
     * If nothing has been done to the hard disks
     * then just get out, back to the desktop.
     * Otherwise reboot the system.
     */
    menu_bar(menuobj, 0);		/* erase menu bar */

punt:
    /*
     * If we have to reboot,
     * tell the user and then do it.
     *
     */
    if (rebootp) {
	err(autoboot);
	reboot();
    }

    if (badbuf > 0L) free(badbuf);
    wind_delete(wi_handle);
    v_clsvwk(handle);
    appl_exit();
}


/*
 * Get a single event, process it, and return.
 *
 */
domulti()
{
    int event;

    event = evnt_multi(MU_MESAG,
			1,1,butdown,
			0,0,0,0,0,
			0,0,0,0,0,
			msgbuff,0,0,&mx,&my,&ret,&ret,&keycode,&ret);

    if (event & MU_MESAG) {
        wind_update(TRUE);
	switch (msgbuff[0]) {
	    case WM_REDRAW:
		do_redraw(msgbuff[4],msgbuff[5],msgbuff[6],msgbuff[7]);
		break;

	    case MN_SELECTED:
	        graf_mouse(HOURGLASS, 0L);
		switch(msgbuff[3]) {
		    case MNDISK:
		        rescan();
			switch (msgbuff[4]) {
			    case SELUNIT:
				dodisunit();
				break;
			    case SELDSK:
				dodisdsk();
				break;
			    case CTRLRTST:
			        dodictrlr();
			        break;
			    case DCHTST:
			        dodidaich();
			        break;
			    case FMTDSK:
			    	dodiform(); 
			    	break;
			    case BRNINTST:
			    	dodibrn();
			    	break;
			    case DEFLST:
			    	dodidef();
			    	break;
			    case SHIPIT:
			        dodiship();
			        break;
			    default:	    break;
			}
			break;

		    case MNFILE:
			switch (msgbuff[4]) {
			    case FIQUIT:
				running = 0;
				break;
			    default:
				break;
			}
			break;
			
		    case MNDESK:
			if(msgbuff[4] == ABOUFHDX) {
			    abtdial[ABOK].ob_state = NORMAL;
			    strcpy(abtdial[VERNUM].ob_spec, "Version 3.8");
			    execform(abtdial, 0);
			    break;
			}
			break;		/* "cannot happen" */
		}

		menu_tnormal(menuobj, msgbuff[3], 1);	/* back to normal */
	    	graf_mouse(ARROW, 0L);
		break;

	    case WM_NEWTOP:
	    case WM_TOPPED:
		wind_set(wi_handle, WF_TOP, 0, 0, 0, 0);
		break;

	    case WM_CLOSED:
		running = FALSE;
		break;

	    default:
		break;
	}
	wind_update(FALSE);
    }
}


/*
 * Handle [SELECT UNIT] item.
 *
 *	Let user select which unit (0 or 1) to operate on.  The default is
 * DEFUNIT.  The selected unit number will be used until another selection
 * is made.
 */
dodisunit()
{
    int i;

    physdial[PHYSOK].ob_state = NORMAL;
    physdial[PHYSCN].ob_state = NORMAL;

    if (execform(physdial, 0) != PHYSOK)
	return BAILOUT;
    
    /* search for selected unit */
    for (i = 0; i < MAXPHYSDEV; ++i) {
	if (physdial[acsiphys[i]].ob_state & SELECTED) {
	    cdev = pdev = i;		/* for ACSI drives */
	    strcpy(cdtype, "ACSI");
	    dsply_utbox();	    
	    i = MAXPHYSDEV;
	} else if (physdial[scsiphys[i]].ob_state & SELECTED) {
	    cdev = i;
	    pdev = cdev + 8;	/* for SCSI drives */
	    strcpy(cdtype, "SCSI");
	    dsply_utbox();	    
	    i = MAXPHYSDEV;
	}
    }
}


/*
 * Handle [SELECT DISK MODEL] item.
 *
 *	Let user select the type of disk to operate on.  The selected 
 * disk type will be used until another selection is made.
 */
dodisdsk()
{
    char pnam[16];	/* disk model name */
    char ndid[3];	/* new disk id */
    
    /* inquire disk model */
    strcpy(ndid, "mn");
    if ((ret = dskmdl(pnam, ndid)) != OK)
        return BAILOUT;
        
    
    /* Get parameters of current disk type and model */
    modesel = 1;
    if (gfparm(&modesel, &hinfo, pnam, ndid) != 0)
	return ERROR;
        
    /* Calculate maximum number of bad sectors allowed */
    maxbadsects 
    	= (int)hinfo.hi_dhc * hinfo.hi_cc * (int)hinfo.hi_spt / MEGABYTE;
    	
    /* Allocate memory for recording bad sectors */
    if (badbuf > 0L) free(badbuf);
    if ((badbuf = (SECTOR *)mymalloc(maxbadsects << 2)) <= 0L) {
    	ret = err(nomemory);
    	goto sdskend;
    }

    strcpy(cdid, ndid);		/* update current disk id */
    strcpy(cdmdl, pnam);	/* update current disk model */
    dsply_utbox();		/* update it on screen */
sdskend:
    ;
}


/*
 * Handle [CONTROLLER TEST] item.
 *
 *	To test the controller, do a quick format on 2 cylinders
 * and 2 heads on the disk.
 */
dodictrlr()
{
    int v, dummy;
    HINFO finfo;	/* disk info for controller test */
    
    graf_mouse(HOURGLASS, 0L);
    v = OK;		/* assume everything is just fine */
    
    if (modesel) {	/* can do mode select? */
        /* if so, do format as controller test */
	if (gfparm(&dummy, &finfo, "TESTER", "ct") != 0)
	    return ERROR;
	    
	ostack = Super(NULL);
	if ((v = ms(pdev, &finfo)) == OK) {		/* mode set */
	    delay();					/* kludge delay */
	    v = format(pdev, (WORD)finfo.hi_in);	/* format */
	}
    } else {
    	/* if not, just do test unit ready */
	ostack = Super(NULL);
    	v = untrdy(pdev);
    }
    delay();						/* kludge delay */
    Super(ostack);
    
    if (v != OK) {
    	ctrlfail[CTRLFOK].ob_state = NORMAL;
    	ctrlfail[CTRLFOK].ob_x = (wdesk - ctrlfail[CTRLFOK].ob_width) >> 1;
    	ctrlfail[CTRFTITL].ob_x = (wdesk - ctrlfail[CTRFTITL].ob_width) >> 1;
        execform(ctrlfail, 0);
    } else {
    	ctrlpass[CTRLPOK].ob_state = NORMAL;
    	ctrlpass[CTRLPOK].ob_x = (wdesk - ctrlpass[CTRLPOK].ob_width) >> 1;
    	ctrlpass[CTRPTITL].ob_x = (wdesk - ctrlpass[CTRPTITL].ob_width) >> 1;
        execform(ctrlpass, 0);
    }
}


/*
 * Handle [DAISY CHAIN TEST] item
 */
dodidaich()
{
    int ndev;		/* device chained to */
    long dp;		/* data pattern to test with */
    long sect2rw;	/* number of sectors read from or write to pdev */
    char *buf, *save;	/* buffers for test pattern and saving info on disk */
    OBJECT *result;	/* resulting dialogue to be put up */
    int but;		/* button on result dialogue */
    int title;		/* title line on result dialogue */
    long num;

    graf_mouse(HOURGLASS, 0L);  
    ndev = pdev + 1;	    /* daisy chain unit = pdev + 1 */
    
    /* Find parameters for daisy chain test in wincap */
    if (wgetent("TESTER", "dc") == ERROR) {
    	return err(nodcent);
    }
    if (wgetnum("rw", &num) == OK)	/* number of sectors to test */
        sect2rw = num;
    else sect2rw = 128;			/* default: 64k */
    if (sect2rw > 128) sect2rw = 128;	/* 128 is maximum */
    
    if (wgetnum("dp", &num) == OK)	/* data pattern to test with */
        dp = num;
    else dp = 0xc623d5ab;
    
    /* Close the wincap file after accessing it */
    wclose();
        	
    if ((buf = (char *)Malloc(sect2rw << 9)) <= 0L
    	|| (save = (char *)Malloc(sect2rw << 9)) <= 0L) {
    	if (buf > 0L) Mfree((long)buf);
        return err(nomemory);
    }
    
    /* fill buffer with test pattern */
    fillbuf(buf, (sect2rw << 9), dp);

    /* Read and save data of model unit */
    if (Lrwabs(PHYSREAD, save, (int)sect2rw, 0L, ndev+2) != 0) {
        result = dchrfail;				/* if failed, exit */
    	but = DCHRERR;
    	title = DCHRTITL;
    	goto dchend;
    }
    
    /* Write data (test pattern) to model unit */
    if (Lrwabs(PHYSWRT, buf, (int)sect2rw, 0L, ndev+2) != 0) {
    	result = dchwfail;				/* if failed, exit */
    	but = DCHWERR;
    	title = DCHWTITL;
    	goto dchend;
    }
    
    /* Read data back from model unit */
    if (Lrwabs(PHYSREAD, buf, (int)sect2rw, 0L, ndev+2) != 0) {
        result = dchrfail;				/* if failed, exit */
    	but = DCHRERR;
    	title = DCHRTITL;
    	goto dchend;
    }
    
    /* Test if data read back == test pattern written */
    if (!blktst(buf, dp, (sect2rw << 9))) {
        result = dchdfail;			/* if wrong, exit */
        but = DCHDERR;
        title = DCHDTITL;
        goto dchend;
    }
    
    /* Write original data back to model unit */
    if (Lrwabs(PHYSWRT, save, (int)sect2rw, 0L, ndev+2) != 0) {
    	result = dchwfail;			/* if failed, exit */
    	but = DCHWERR;
    	title = DCHWTITL;
    	goto dchend;
    }
    
    /* Everything is ok */
    result = dchpass;
    but = DCHPOK;
    title = DCHPTITL;
dchend:
    Mfree((long)buf);
    Mfree((long)save);
    result[but].ob_state = NORMAL;	/* display result */
    execform(result, 0);
}


/* Default partition name (no "pt" entry) */
#define	DEF_PARTNAME	"4-6-10"


/*
 * Handle [FORMAT] item.
 *
 */
dodiform()
{
    extern char bootstop;
    extern char bootend;
    extern long get3bytes();
    int v, i, ret;
    long cnt, entries, hdsiz;
    long *a, *b;
    char *s, *d, *wgetstr();
    char sendata[32];
    
    
    /* Get size of Bad Sector List */
    bslsiz = gbslsiz(pdev);
        
    bsl = 0L;
    if (bslsiz > 0) {
	/* Allocate memory for existing BSL */
	if ((bsl = (BYTE *)mymalloc((int)bslsiz << 9)) <= 0) {
	    ret = err(nomemory);
	    goto fmtend;
	}
      
	/* Read in BSL */
	if (rdbsl(pdev) != OK) {
	    /* Create a new BSL if current one is unusable */
	    if (creabsl(pdev, NEW, 0L) != OK) {
		ret = ERROR;
		goto fmtend;
	    }
	} else {
	    entries = nument(VENDOR);
	    if (creabsl(pdev, EXPAND, entries) != OK) {
		ret = ERROR;
		goto fmtend;
	    }
	}
    } else {
	if (creabsl(pdev, NEW, 0L) != OK) {
	    ret = ERROR;
	    goto fmtend;
	}
    }
  
    /*
     * In supervisor mode
     * set disk format parameters
     * and format the disk.
     */
    v = OK;			/* assume everything is ok */
    hdsiz = 0L;			/* initialise size to zero */
    
    /* For REAL !! */  
    dsplymsg(fmtmsg);
    ostack = Super(NULL);
    if (modesel) {  		/* normal mode select? */
	v = ms(pdev, &hinfo);	/* Yes, do normal mode set */
    } else {			/* No, do special mode set */
	if ((mdsense(pdev, 0, 0, 32, sendata)) == OK) {
	    if ((hdsiz = get3bytes(sendata+5)))	{ /* for Microscience sake */
	    	delay();
		v = scsims(pdev, sendata);
	    }
	}
    }
    
    delay();					/* kludge delay */
    if (v == OK)  
	v = format(pdev, (WORD)hinfo.hi_in);	/* format */
    delay();					/* kludge delay */
    Super(ostack);				/* back to user mode */
    rebootp = 1;
    erasemsg();		/* wipe out format message on screen */
    
    if (v != 0) {
	ret = formaterr(cdev, cdtype);
	goto fmtend;
    }

    /* Write BSL back to disk */  
    if ((ret = wrbsl(pdev)) != OK) {
	goto fmtend;
    }

    /*
     * Install format parameters and boot-stopper in sector image;
     * write root sector to device.
     */
    fillbuf(bs, 512L, 0L);		/* make a clean root sector */
    sfmtparm(bs, &hinfo);		/* set soft format parms */
    					/* record disk type */
    strcpy((RSECT *)(bs + 0x200 - sizeof(RSECT))->hd_type, cdmdl);
    					/* record disk size */
    if (!hdsiz) {
	hdsiz = (long)hinfo.hi_cc * (long)hinfo.hi_dhc * (long)hinfo.hi_spt;
    }
    (RSECT *)(bs + 0x200 - sizeof(RSECT))->hd_siz = hdsiz;
    sbslparm(bs);			/* set BSL parameters */
    for (d = bs, s = &bootstop, cnt = &bootend - &bootstop; --cnt;)
	*d++ = *s++;
  
    Protobt(bs, -1L, -1, 1);		/* make root sector executable */
    if ((ret = putroot(pdev, bs)) == ERROR) {
	ret = err(rootwrit);
	goto fmtend;
    }

    /* Do a little test just to make sure root sector is OK */
    if ((ret = getroot(pdev, sbuf)) == ERROR) {
	ret = err(rootread);
    	goto fmtend;
    }  
    
    /* Compare root sector read in has the same data as those written in */
    a = (long *)bs;
    b = (long *)sbuf;
    for (i = 0; i < 512; i+=4, a++, b++) {
        if (*a != *b) {
            ret = err(rootbad);
            goto fmtend;
        }
    }
    
    /* 
     * Partition the device (using the default partition)
     */
    free(bsl);
    if ((ret = dodipart()) < 0) {
    	if (ret == USRFULL)
            ret = err(deffull);
        goto fmtend;
    }
    ret = OK;
fmtend:
    return ret;
}



/*
 * Just partition pdev using the default partition scheme of cdmdl
 *
 */
dodipart()
{
    int  spc, i, ret =OK;
    PART pinfo[4];
    long hdsiz;

    graf_mouse(HOURGLASS, 0L);
    dsplymsg(partmsg);
    
    if (getroot(pdev, bs) != OK) {
	ret = err(rootread);
	goto partend;
    }

    /* find hard disk size */
    hdsiz = ((RSECT *)(bs + 0x200 - sizeof(RSECT)))->hd_siz;
    
    /* allow 1 bad sector per Mb */
    maxbadsects = hdsiz / MEGABYTE;

    if (wgetent(cdmdl, "pr") != OK) {	/* if can't find entry */
    	erasemsg();
    	strcpy(nopart[NOSCHPR].ob_spec, cdmdl);
	nopart[NOSCHPOK].ob_state = NORMAL;
	execform(nopart, 0);
	return ERROR;
    }

    for (i = 0; i < NPARTS; ++i)
	fillpart(i, &pinfo[i]);
	    
    /* Close the wincap file after accessing it */
    wclose();
        	
    /* Get size of BSL */
    bslsiz = gbslsiz(pdev);
    
    bsl = 0L;
    if (bslsiz > 0) {
    	/* Allocate memory for existing BSL */
    	if ((bsl = (BYTE *)mymalloc((int)(bslsiz << 9))) <= 0) {
    	    ret = err(nomemory);
    	    goto partend;
    	}
    	    
    	/* Read in BSL */
    	if ((ret = rdbsl(pdev)) != OK) {
    	    if (ret == INVALID)
    	        err(cruptbsl);
    	    ret = ERROR;
    	    goto partend;
    	}
    } else if (bslsiz < 0) {
        ret = err(rootread);
        goto partend;
    } else {			/* bslsiz == 0;  shouldn't happen at all */
        ret = err(notfmtd);
        goto partend;
    }
    
    /* Lay out partition headers */
    spc = hinfo.hi_dhc * hinfo.hi_spt;
    if (pheader(pdev, pinfo, spc) != OK) {
        ret = ERROR;
        goto partend;
    }

    if (nument(VENDOR) > maxbadsects) {
        ret = USRFULL;
        goto partend;
    }    
    
    if (wrbsl(pdev) != OK) {		/* write BSL */
    	ret = err(bslwrite);
    	goto partend;
    }
    
    /*
     * Shove partition parms into root sector.
     */
    spart(bs, pinfo);
    sbslparm(bs);			/* set BSL parameters */
    Protobt(bs, -1L, -1, 1);		/* make root sector executable */
    if (putroot(pdev, bs) != OK) {
    	ret = err(rootwrit);
    	goto partend;
    }

    rebootp = 1;
    
    /* Partition the device with parameters given in pinfo */
    rescan();
    /* dopart() displays its own error msg */
    if (dopart(pdev, hinfo.hi_spt, pinfo, hdsiz) < 0)
        ret = ERROR;
    else
	ret = OK;
partend:
    if (bsl > 0) free(bsl);
    rescan();
    erasemsg();
    return (ret);
}


/*
 *  Physical Markbad for Disk Testing.
 */
dodibrn()
{
    long pattern, num;		/* pattern being used for testing */
    int  done=0, ret;
    int  but, title;		/* button and title of result dialogue */
    OBJECT *result;

    graf_mouse(HOURGLASS, 0L);

    /* Get size of Bad Sector List */
    bslsiz = gbslsiz(pdev);
        
    bsl = 0L;
    if (bslsiz > 0) {
	/* Allocate memory for existing BSL */
	if ((bsl = (BYTE *)mymalloc((int)bslsiz << 9)) <= 0L) {
	    ret = err(nomemory);
	    goto brnend;
	}
      
	/* Read in BSL */
	if (rdbsl(pdev) != OK) {
	    /* Create a new BSL if current one is unreadable */
	    if (creabsl(pdev, NEW, 0L) != OK) {
	        ret = ERROR;
	        goto brnend;
	    }
	} else {
            /* Remove USER BSL */
      	    if (creabsl(pdev, EXPAND, nument(VENDOR)) != OK) {
      	        ret = ERROR;
      	        goto brnend;
      	    }
	}
    } else if (bslsiz == 0) {
	ret = err(notfmtd);
	goto brnend;
    } else {	/* bslsiz < 0 */
        ret = err(rootread);
        goto brnend;
    }

    /* save the root sector */
    if (getroot(pdev, bs) != OK) {
    	ret = err(rootread);	/* cannot read root sector */
    	goto bfail;
    }  
    
    ret = dsmarkbad(&hinfo, pdev, pattern);
    if (wrbsl(pdev) != OK) {
    	ret = err(bslwrite);
    	goto bfail;
    }
    
    sbslparm(bs);			/* set BSL parameters */        
    if (putroot(pdev, bs) == ERROR) {	/* put root sector back to disk */
    	ret = err(rootwrit);
    	goto bfail;
    }
    
    if (ret >= 0 && ret <= maxbadsects) {
	if (bsl > 0) free(bsl);
	if(dodipart() < 0)
	    goto bfail;

	/* set up result box for passing disks */
	result = dskpass;
	but = BRNPASS;
	title = BRNPTITL;
	goto dsply;
    }
    
bfail:
    /* set up result box for failing disks */
    result = dskfail;
    but = BRNFAIL;
    title = BRNFTITL;
dsply:
    result[but].ob_state = NORMAL;
    execform(result, 0);

    if (bsl > 0) free(bsl);
    if ((bslsiz = gbslsiz(pdev))> 0)
	dsplybsl(pdev, (unsigned int)hinfo.hi_dhc,
		 (unsigned int)hinfo.hi_spt);

brnend:
    if (bsl > 0) free(bsl);
    return ret;
}


/*
 *  Key-in bad sector list in terms of physical block address.
 *  (i.e. head number, cylinder number and byte offset from index)
 *  Sector numbers are calculated and added to the BSL.
 */
dodidef()
{
    int done=0, ret=0;
    int pbps;
    extern int npndefs;		/* number of defects entered */
    
    /* Get size of Bad Sector List */
    bslsiz = gbslsiz(pdev);	/* always device 0 */
        
    bsl = 0L;
    if (bslsiz > 0) {
	/* Allocate memory for existing BSL */
	if ((bsl = (BYTE *)mymalloc((int)bslsiz << 9)) <= 0) {
	    ret = err(nomemory);
	    goto defend;
	}
	
	/* Read in BSL */
	if (rdbsl(pdev) != OK) {
	    /* Create a new BSL if current one is unusable */
	    if (creabsl(pdev, NEW, 0L) != OK) {
		ret = ERROR;
		goto defend;
	    }
	    goto defstrt;
	} else {
	    if (!(nument(VENDOR)))
	        goto defstrt;
	}
	
    	/* Ask if they want to keep existing one */
    	keepdefs[KEEPYES].ob_state = NORMAL;
    	keepdefs[KEEPNO].ob_state = NORMAL;
    	if (execform(keepdefs, 0) == KEEPNO) {
	    if (creabsl(pdev, NEW, 0L) != OK) {
		ret = ERROR;
		goto defend;
	    }
	}
    } else if (bslsiz == 0) {
	ret = err(notfmtd);
	goto defend;
    } else {	/* bslsiz < 0 */
        ret = err(rootread);
        goto defend;
    }
defstrt:
    if (figtst() != NPUTSTRT) {
        ret = BAILOUT;
        goto defend;
    }

    /* Figure out maximum bytes per track of media */
    if (hinfo.hi_spt == 0x11) {	/* 17 spt: disk is MFM format */
        fmt = MFM;
    	pbps = BPSMFM;
    } else {	       			/* disk is RLL format */
        fmt = RLL;
    	pbps = BPSRLL;
    }
        
    /* Find maximum size of head, cylinder and byte offset */
    maxpaddr.head = (unsigned int)hinfo.hi_dhc;
    maxpaddr.cylndr = (unsigned)hinfo.hi_cc;
    if (tst == MFM)
        maxpaddr.btoffst = BPTMFM;
    else
        maxpaddr.btoffst = BPTRLL;

    /* Let user input defects */
    if (nputdeflst() != OK) {
    	ret = ERROR;
        goto defend;
    }
    
    /* Give user chances to examine and modify the defects entered */
    while (TRUE) {
	askxam[XAMXAM].ob_state = NORMAL;
	askxam[XAMSAVE].ob_state = NORMAL;
	askxam[XAMQUIT].ob_state = NORMAL;
	if ((ret = execform(askxam, 0)) == XAMXAM) {	/* Examine defects */
	    if (!npndefs) {
		err(defemp);	/* defect list is empty */
	    } else {
	    	graf_mouse(ARROW, 0L);
		xamdeflst();
	    }
	} else {
	    break;
	}
    }
    
    if (ret == XAMQUIT) {
        ret = OK;
        goto defend;
    }
        
    /* Check if any defect is actually entered */
    if (npndefs) {
        /* Calculate sector numbers from input defects and add them to BSL */
        if((npndefs = calcsect(&hinfo, pbps)) < 0) {
            ret = ERROR;
            goto defend;
        }
        
        /* Add sector numbers to BSL */
        if ((ret = addbsl(pdev, VENDOR, npndefs)) < 0) {
            ret = ERROR;
            goto defend;
        }
    }

    /* Read root sector */    
    if (getroot(pdev, bs) != OK) {
	ret = err(rootread);
    	goto defend;
    }  
    sbslparm(bs);		/* set BSL parameters */
    /* Put root sector back to disk, and repartition disk. */
    if (putroot(pdev, bs) == ERROR) {
        ret = err(rootwrit);
        goto defend;
    }
        
    if (wrbsl(pdev) != OK) {
    	ret = err(bslwrite);
    	goto defend;
    }
    	
    free(bsl);
    if ((ret = dodipart()) < 0) {
    	if (ret == USRFULL)
            ret = err(deffull);
        goto defend;
    }
    ret = OK;
defend:
    if (bsl > 0L) free(bsl);
    return ret;
}


/*
 *  Ship the disk.
 */
dodiship()
{
    graf_mouse(HOURGLASS, 0L);
    ship(pdev);
    err(scommand);
}



/*
 * Display current unit and current disk type on menu bar.
 */
dsply_utbox()
{
    strcpy(unittype[CURRTYPE].ob_spec, cdtype);
    itoa(cdev, unittype[CURRUNIT].ob_spec->te_ptext);
    strcpy(unittype[CURRMDL].ob_spec->te_ptext, cdmdl);
    objc_draw(unittype, 0, MAX_DEPTH, xdesk, unittype[0].ob_y, wdesk, 
    		hdesk+gl_hbox);
}
    		
/*
 * Map from button in format dial.
 */
int pfmt[] = {
    PFMT0, PFMT1, PFMT2, PFMT3,
    PFMT4, PFMT5, PFMT6, PFMT7,
    PFMT8, PFMT9, PFMT10, PFMT11,
    PFMT12, PFMT13, PFMT14
};
        
/*
 *  Request the disk type from user.
 */
dskmdl(name, ent_id)
char name[];
char ent_id[];
{
    char devnames[NAMSIZ];	/* device type name buffer */
    char *s;
    int  i, br, ret;
    
    br = 0;			/* not processing a branch */
inqfmt:
    /* Get all available disk types from wincap 'ent_id' entries */  
    if (wallents(devnames, ent_id) != OK) {
        ret = ERROR;
        goto dskend;
    }
  
    /* Shove format name text into buttons */
    for (i = 0, s = devnames; i < 15 && *s; ++i) {
	dsknames[pfmt[i]].ob_type = G_BUTTON;	/* buttons */
	dsknames[pfmt[i]].ob_spec = (long)s;
	dsknames[pfmt[i]].ob_state = NORMAL;
	dsknames[pfmt[i]].ob_flags = SELECTABLE | RBUTTON;
	while (*s++)
	    ;
    }
  
    /* rest of buttons are invisible and untouchable */
    for (; i < 15; ++i) {
	dsknames[pfmt[i]].ob_type = G_IBOX;	/* invisible box */
	dsknames[pfmt[i]].ob_spec = 0;		/* no thickness */
	dsknames[pfmt[i]].ob_state = DISABLED;	/* nobody home */
	dsknames[pfmt[i]].ob_flags = NONE;	/* disabled */
    }
  
    /* clean up rest of the form and throw it up */
    dsknames[PFOK].ob_state = NORMAL;
    dsknames[PFCN].ob_state = NORMAL;
    if (execform(dsknames, 0) != PFOK) {
	ret = BAILOUT;
	goto dskend;
    }
  
    /* search for format they picked */
    for (i = 0; i < 15; ++i)
	if (dsknames[pfmt[i]].ob_state & SELECTED)
	    break;
	    
    if (i >= 15) {
    	ret = BAILOUT;			/* nothing picked */
    	goto dskend;
    }
    
    if (!br) {
	if (wgetent(dsknames[pfmt[i]].ob_spec, "mn") == ERROR) {
    	    strcpy(nofmt[NOSCHFMT].ob_spec, dsknames[pfmt[i]].ob_spec);
    	    nofmt[NOSCHFOK].ob_state = NORMAL;
    	    execform(nofmt, 0);
	    ret = ERROR;
	    goto dskend;
	}
    
	if ((s = wgetstr("br")) != NULL) {
	    strcpy(ent_id, s);	/* id for branch */
	    br = 1;		/* processing the branch */
	    goto inqfmt;	/* start over */
	}
    }
    strcpy(name, dsknames[pfmt[i]].ob_spec);
    ret = OK;
    
dskend:
    /* Close the wincap file after accessing it */
    wclose();
        	
    return ret;
}
  

/*
 * Open virtual workstation.
 *
 */
open_vwork()
{
    int i;

    for (i = 0; i < 9;)
	work_in[i++] = 1;
    work_in[10] = 2;
    handle = phys_handle;
    v_opnvwk(work_in, &handle, work_out);
}


/*
 * Find and redraw all clipping rectangles
 *
 */
do_redraw(xc, yc, wc, hc)
int xc, yc, wc, hc;
{
    GRECT t1, t2;
    int temp[4];

    wind_update(TRUE);
    hide_mouse();
    t2.g_x=xc;
    t2.g_y=yc;
    t2.g_w=wc;
    t2.g_h=hc;
    vsf_interior(handle, 1);
    vsf_color(handle, 0);
    wind_get(wi_handle, WF_FIRSTXYWH, &t1.g_x, &t1.g_y, &t1.g_w, &t1.g_h);
    while (t1.g_w && t1.g_h) {
	if (rc_intersect(&t2, &t1)) {
	    set_clip(t1.g_x, t1.g_y, t1.g_w, t1.g_h);
	    temp[0] = xdesk;
	    temp[1] = ydesk;
	    temp[2] = xdesk + wdesk - 1;
	    temp[3] = ydesk + hdesk - 1;
	    v_bar(handle, temp);
	}
	wind_get(wi_handle, WF_NEXTXYWH, &t1.g_x, &t1.g_y, &t1.g_w, &t1.g_h);
    }

    show_mouse();
    wind_update(FALSE);
}


/*
 * Hide the mouse.
 *
 */
hide_mouse()
{
    if (!hidden) {
	graf_mouse(M_OFF, 0L);
	hidden = TRUE;
    }
}


/*
 * Show the mouse.
 *
 */
show_mouse() 
{
    if (hidden) {
	graf_mouse(M_ON, 0L);
	hidden = FALSE;
    }
}


/*
 * Set clipping rectangle.
 *
 */
set_clip(x, y, w, h)
int x, y, w, h;
{
    int clip[4];

    clip[0] = x;
    clip[1] = y;
    clip[2] = x + w;
    clip[3] = y + h;
    vs_clip(handle, 1, clip);
}



/*
 * Complain about corrupt resource file
 * and setup to return to the desktop.
 *
 */
corrupt()
{
    err(rscorrupt);				/* complain */
    running = FALSE;				/* stop multi calls */
    return ERROR;				/* bubble up complaint */
}


/*
 * "Execute" form,
 * return thingy that caused the exit.
 *
 */
execform(tree, st_ob)
OBJECT tree[];
int st_ob;
{
    int thingy;

    graf_mouse(ARROW, 0L);
    dsplymsg(tree);
    thingy = form_do(tree, st_ob);
    erasemsg();
    graf_mouse(HOURGLASS, 0L);
    return thingy;
}


/*
 *  Display a dialogue box on the screen.
 *	Input:
 *		tree - object tree for dialogue box to be displayed.
 *	Output:
 *		formw, formh, sx, sy, lx, ly - dimensions of box.
 */
dsplymsg(tree)
OBJECT tree[];
{
    formw = tree[0].ob_width;
    formh = tree[0].ob_height;
    
    sx = wdesk / 2;
    sy = hdesk / 2;
    if ((lx = (wdesk - formw) / 2) < 0)
        lx = xdesk;
    if ((ly = (hdesk - formh) / 2) < 0)
        ly = ydesk;
    
    tree[0].ob_x = lx;
    tree[0].ob_y = ly;
    
    form_dial(0, sx, sy, 0, 0, lx, ly, formw, formh);
    objc_draw(tree, 0, MAX_DEPTH, xdesk, ydesk, wdesk, hdesk);
}


/*
 *  Erase a dialogue box from the screen.
 *	Input:
 *		formw, formh, sx, sy, lx, ly - dimensions of box.
 */
erasemsg()
{
    form_dial(3, sx, sy, 0, 0, lx-3, ly-3, formw+4, formh+4);
}



/*
 *  Make a long (4-byte) random.
 */ 
long
longrandom()
{
    long pattern;
    
    pattern = Random();
    pattern <<= 16;
    pattern ^= Random();
    
    return (pattern);
}

