/***************************************************************************/
/*           INSTALL A CUSTOM BOOTBLOCK TO ANY DISK                        */
/* Compiler: SAS/C  V6.51                                           ORHAN  */
/***************************************************************************/
#define __USE_SYSBASE

#include <exec/types.h>
#include <exec/memory.h>
#include <exec/io.h>
#include <exec/ports.h>
#include <dos/dos.h>
#include <dos/filehandler.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <devices/trackdisk.h>
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/utility.h>

long   SetBoot(UBYTE *bootblock, ULONG length, LONG key);
void   ClrBoot(UBYTE *bootblock, ULONG length);
long   BootChkSum(UBYTE *bootblock, ULONG length);
void   PutDosType(ULONG *bootblock);
void   GetLenBoot(void);
struct DeviceNode *findentry(struct DeviceNode *dn, UBYTE *name, LONG type);
long   readsector(UBYTE *buf, ULONG offset, ULONG length);
long   writesector(UBYTE *buf, ULONG offset, ULONG length);
long   update(void),motoron(void),motoroff(void);

struct DosInfo     *di;
struct DeviceNode  *dn;
struct DosEnvec    *de;
struct FileSysStartupMsg *fssm;
struct IOStdReq    *devreq=NULL;
struct MsgPort     *devport=NULL;
struct RDArgs      *rdargs=NULL;
struct InfoData    *infodata=NULL;
struct FileLock    *flock;
UBYTE  version[]   ="$VER: InstallGen 1.0 (12.01.95) © by OR\r\n";
enum   {ARG_DRIVE,ARG_NOBOOT,ARG_CHECK,ARGCOUNT};
LONG   myarray[ARGCOUNT];
UBYTE  *devname;
UWORD  openstatus=NULL;
UBYTE  *buffer=NULL;
ULONG  lenbuf,offset;
ULONG  lenboot=NULL;
BPTR   lock=NULL;
LONG   rc=NULL,rcio=NULL;
UBYTE  *str0 = "there is no bootblock\n";
UBYTE  *str1 = "not bootable";
UBYTE  *str2 = "bootblock ok";
UBYTE  *str3 = "bootblock size invalid\n";


long installgen(void)
{
       SysBase = (*((struct ExecBase **)0x4));
       DOSBase = (struct DosLibrary *)OpenLibrary("dos.library",36);
       if (DOSBase==NULL) goto cleanexit;
       UtilityBase = OpenLibrary("utility.library",NULL);
       if (UtilityBase==NULL) goto cleanexit;
      
       rdargs = ReadArgs("DRIVE/A,"
                         "NOBOOT/S,"
                         "CHECK/S",&myarray[0],NULL);

       if (!rdargs) {rc=IoErr(); goto cleanexit;}

       Forbid();
       di=(struct DosInfo *)BADDR(DOSBase->dl_Root->rn_Info);
       dn = findentry((struct DeviceNode *)&di->di_DevInfo,(UBYTE *)myarray[ARG_DRIVE],DLT_DEVICE);
       Permit();

       if (dn==DOSFALSE) {rc=ERROR_OBJECT_NOT_FOUND; goto cleanexit;}

       fssm=(struct FileSysStartupMsg *)BADDR(dn->dn_Startup);
       if ((ULONG)fssm<0x400 || dn->dn_Task==NULL)
               {rc=ERROR_OBJECT_WRONG_TYPE; goto cleanexit;}

       de = (struct DosEnvec *)BADDR(fssm->fssm_Environ); 
       if (de->de_TableSize<9) {rc=ERROR_OBJECT_WRONG_TYPE; goto cleanexit;}

       offset = de->de_SizeBlock*4 *
                de->de_BlocksPerTrack * 
                de->de_Surfaces *
                de->de_LowCyl;

       lenbuf = de->de_SizeBlock*4*de->de_Reserved;
       if (lenbuf==0) {rc=ERROR_OBJECT_WRONG_TYPE; goto cleanexit;}

       buffer=AllocVec(lenbuf,de->de_BufMemType);
       infodata=AllocVec(sizeof(struct InfoData),MEMF_PUBLIC);
       devport = CreateMsgPort();
       devreq = CreateIORequest(devport,sizeof(struct IOStdReq));
       if (buffer==NULL || devport==NULL || devreq==NULL || infodata==NULL)
                            {rc=ERROR_NO_FREE_STORE; goto cleanexit;}

       devname=(UBYTE *)BADDR(fssm->fssm_Device); devname++;
       rcio = OpenDevice(devname,fssm->fssm_Unit,(struct IORequest *)devreq,fssm->fssm_Flags);
       if (rcio) goto cleanexit; openstatus=1;

       if (myarray[ARG_CHECK]==NULL) {
       lock=Lock((UBYTE *)myarray[ARG_DRIVE],ACCESS_READ);
       if (lock==NULL) {rc=IoErr(); goto cleanexit;}
       if (Info(lock,infodata)==DOSFALSE) {rc=IoErr(); goto cleanexit;}
       flock=BADDR(lock);
       GetLenBoot(); if (lenboot==0) goto cleanexit;
       if (infodata->id_DiskState==ID_WRITE_PROTECTED)
                           {rc=ERROR_DISK_WRITE_PROTECTED; goto cleanexit;}
       rcio = motoron(); if (rcio) goto cleanexit;
                                     }

       rcio = readsector(buffer,offset,lenbuf); if (rcio) goto cleanexit;


       if (myarray[ARG_CHECK]) {
       PutStr("Appears to be "); PutDosType((ULONG *)buffer); PutStr(" disk, ");
       GetLenBoot(); if (lenboot==0) goto cleanexit;
       if (BootChkSum(buffer,lenboot)) PutStr(str1); else PutStr(str2);
       VPrintf(", its size %ld bytes\n",&lenboot);
                               }

       else if (myarray[ARG_NOBOOT]) {
       ClrBoot(buffer,lenboot);
       rcio = writesector(buffer,offset,lenboot); if (rcio) goto cleanexit;
       rcio = update();                           if (rcio) goto cleanexit;
       PutStr("Bootblock cleared\n");
                                     }

       else if (myarray[ARG_DRIVE]) {
       rcio = SetBoot(buffer,lenboot,flock->fl_Key); if (rcio) goto cleanexit;
       rcio = writesector(buffer,offset,lenboot);    if (rcio) goto cleanexit;
       rcio = update();                              if (rcio) goto cleanexit;
       PutStr("Bootblock installed\n");
                                    }


cleanexit:
        if (openstatus) {motoroff(); CloseDevice((struct IORequest *)devreq);}
        if (devreq) DeleteIORequest(devreq);
        if (devport) DeleteMsgPort(devport);
        if (lock) UnLock(lock);
        if (rdargs) FreeArgs(rdargs);

        if (rc==0 && rcio==TDERR_DiskChanged) {rc=ERROR_NO_DISK; rcio=0;}
        if (rc) PrintFault(rc,NULL); else if (rcio>0) rc=rcio;
        if (rcio<0) PutStr("Bootblock is very small\n");
        if (rcio>0) PutStr("Unable to read from or write to volume\n");

        if (UtilityBase) CloseLibrary(UtilityBase);
        if (DOSBase) CloseLibrary((struct Library *)DOSBase);
        if (buffer) FreeVec(buffer);
        if (infodata) FreeVec(infodata);
        return(rc);
}


void GetLenBoot(void)
{
       if (de->de_TableSize<19) PutStr(str0);
       else {lenboot = de->de_SizeBlock*4*de->de_BootBlocks;
             if (lenboot==0 || de->de_BootBlocks > de->de_Reserved)
                                                 PutStr(str3);
            }
}


struct DeviceNode *findentry(struct DeviceNode *dn, UBYTE *name, LONG type)
{
UBYTE *name2;
ULONG len;
         /* first node is initial ptr to DosList !*/

       while(dn=(struct DeviceNode *)BADDR(dn->dn_Next)) { 
           name2 = BADDR(dn->dn_Name); len=*name2++;
           if (dn->dn_Type == type && 
               Strnicmp(name,name2,len)==0 &&
               *(name+len)==':') break;
                                                         }

      return(dn);
}


void PutDosType(ULONG *buf)
{
          switch(*buf) {
      case ID_DOS_DISK:         PutStr("OFS"); break;		
      case ID_FFS_DISK:         PutStr("FFS"); break;		
      case ID_INTER_DOS_DISK:   PutStr("OFS-INT"); break;	
      case ID_INTER_FFS_DISK:   PutStr("FFS-INT"); break;	
      case ID_FASTDIR_DOS_DISK: PutStr("OFS-DC"); break;	
      case ID_FASTDIR_FFS_DISK: PutStr("FFS-DC"); break;	
              default:          PutStr("unknown"); break;	
                       }
}


long motoron(void)
{
     devreq->io_Command = TD_MOTOR;
     devreq->io_Length = 1;
     return(DoIO((struct IORequest *)devreq));
}


long motoroff(void)
{
     devreq->io_Command = TD_MOTOR;
     devreq->io_Length = 0;
     return(DoIO((struct IORequest *)devreq));
}


long update(void)
{
     devreq->io_Command = CMD_UPDATE;
     return(DoIO((struct IORequest *)devreq));
}


long readsector(UBYTE *buf, ULONG offset, ULONG length)
{
     devreq->io_Length = length;
     devreq->io_Data = (APTR)buf;
     devreq->io_Command = CMD_READ;
     devreq->io_Offset = offset;
     return(DoIO((struct IORequest *)devreq));
}


long writesector(UBYTE *buf, ULONG offset, ULONG length)
{
     devreq->io_Length = length;
     devreq->io_Data = (APTR)buf;
     devreq->io_Command = CMD_WRITE;
     devreq->io_Offset = offset;
     return(DoIO((struct IORequest *)devreq));
}
/*
*/
