/**********************************************/
/**********************************************/
/***                                        ***/
/***          BAMCOPY VERSION 1.0           ***/
/***                                        ***/
/***    WRITTEN BY OLIVER CZESLA IN 1987    ***/
/***                                        ***/
/***          AMIGA 500/1000/2000           ***/
/***                                        ***/
/***       Copyright KICKSTART 10.87        ***/
/**********************************************/
/**********************************************/

#include <exec/io.h>
#include <exec/ports.h>
#include <libraries/dosextens.h>
#include <graphics/gfx.h>
#include <graphics/rastport.h>
#include <graphics/copper.h>
#include <graphics/view.h>
#include <graphics/gfxbase.h>
#include <graphics/gfxmacros.h>
#include <graphics/text.h>
#include <exec/types.h>
#include <exec/tasks.h>
#include <exec/nodes.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
#include <devices/trackdisk.h>
#include <exec/lists.h>
#include <exec/memory.h>

#define TD_READ      CMD_READ
#define BLOCKSIZE    TD_SECTOR
#define BSIZE        (TD_SECTOR * NUMSECS)
#define NUMBLOCKS    (NUMCYLS * NUMHEADS * NUMSECS)
#define ROOTBLOCK    (NUMBLOCKS/2)
#define LWORDS       (NUMBLOCKS/32)

extern APTR AllocMem();
extern struct TextFont  *OpenDiskFont();
extern struct MsgPort   *CreatePort();
extern struct IORequest *CreateExtIO();

struct Screen        *Screen        = NULL;
struct MsgPort       *diskport;
struct IOExtTD       *diskreq;
struct RastPort      *rp;
struct ViewPort      *vp;
struct UCopList      *cl;
struct IntuitionBase *IntuitionBase = NULL;
struct GfxBase       *GfxBase       = NULL;

struct TextAttr font = { (STRPTR) "topaz.font",8,0,0 };
struct NewScreen MyScreen = { 0,0,320,200,1,0,1,0,CUSTOMSCREEN,&font,0,0,0 };
struct Process *proc, *FindTask();

ULONG  *diskbuffer;

int    i,counter,blocks[1761],dr,cy2,he2,se2,ch1,ch2,ver=0,mul=0;
int    ch1old,ch2old,cylold,heold,seold;
long   wbuf,bfsize;
short  error;
char   buf[60];

UWORD                         /* Farbpalette Vorder- und Hintergrund */
cola[8] = { 0xfbe,0xeae,0xe9e,0xc8d,0xb7d,0xa6c,0x86b,0x75b },
colb[3] = { 0x555,0x444,0x333 };

closescreens()
{
   struct Screen *scr,*scr2;
   struct Window *win,*win2;

   win=IntuitionBase->ActiveWindow;
   while(win!=0)
   {
      win2=win->NextWindow;
      CloseWindow(win);
      win=win2;
   }

   scr=IntuitionBase->FirstScreen;
   while(scr!=0)
   {
      scr2=scr->NextScreen;
      if(scr!=Screen) CloseScreen(scr);
      scr=scr2;
   }
}

box(x,y,x1,y1)
long x,y,x1,y1;
{
  Move(rp,x,y);
  Draw(rp,x1,y);
  Draw(rp,x1,y1);
  Draw(rp,x,y1);
  Draw(rp,x,y);
}

place(x,y,tx)
long x,y;
char *tx;
{
   Move(rp,x,y+6);
   Text(rp,tx,strlen(tx));
}

doit(nr,by)
short nr,by;
{
   diskreq->iotd_Req.io_Length=by;
   if(nr) diskreq->iotd_Req.io_Command=TD_REMOVE; /* Diskinterrupt aus */
   else diskreq->iotd_Req.io_Command=TD_MOTOR;    /* Motor an/aus      */
   DoIO(diskreq);
}

ReadBlock(sector)
long sector;
{
   LONG offset;
   diskreq->iotd_Req.io_Length=BLOCKSIZE;
   diskreq->iotd_Req.io_Data=diskbuffer;
   diskreq->iotd_Req.io_Command=CMD_READ;
   offset=TD_SECTOR*sector;
   diskreq->iotd_Req.io_Offset=offset;
   DoIO(diskreq);
   return (0);
}

rwc(a,cyl, sec, hd, mem)   /* read or write cylinder */
SHORT a,cyl, sec, hd;
LONG mem;
{
   LONG offset;
   short e;
   if(a)
   {
      diskreq->iotd_Req.io_Length=BSIZE;
      diskreq->iotd_Req.io_Command=TD_FORMAT;
   }
   else
   {
      diskreq->iotd_Req.io_Length=BLOCKSIZE;
      diskreq->iotd_Req.io_Command=CMD_READ;
   }
   diskreq->iotd_Req.io_Data=mem;
   offset=TD_SECTOR*(sec+NUMSECS*hd+NUMSECS*NUMHEADS* cyl);
   diskreq->iotd_Req.io_Offset = offset;
   DoIO(diskreq);

#asm
        bchg #1,$bfe001  ; blinken der systemled
#endasm

   e=diskreq->iotd_Req.io_Error;
   if(e!=0) displayerr(e);
}

getbam()
{
   register long check,k,err;
   register short n,j,l,cy1,he1,se1;
   register int v1;
   int bmsect;

   err=OpenDevice(TD_NAME,0,diskreq,0);
   doit(0,1);
   ReadBlock (879);
   ReadBlock (880);  /* rootblock */
   name();
   bmsect = diskbuffer[79];
   check=diskbuffer[78];
   if(check!=-1) place(109,110,"DISK NOT VALIDATED");
   ReadBlock (bmsect);
   doit(0,0);
   counter=v1=0;
   blocks[counter++] = 0;
   blocks[counter++] = 1;

   for (j=1; j<=LWORDS; j++)
   {
      k = diskbuffer[j];
      for (n=0; n<32; n++)
      if (j<LWORDS || n<30)
      {
         if (~k & 1)
         {
            l = (j-1 << 5) + n + 2;
            cy1=(l/22);
            se1=( l % 22);
            if(se1>=11)
            {
               se1-=11;
               he1=1;
            }
            else he1=0;
            v1=(((v1 & 0) | cy1)<<1);
            v1=((v1 | he1)<<4);
            v1=(v1 | se1);
            blocks[counter]=v1;
            counter++;
         }
         k>>=1;
      }
   }
}

name()
{
   long d,adr;
   int i;
   char *c;

   adr= &diskbuffer[108];
   d = (*(int *)adr)>>24;
   if(d>=25) d=25;
   adr+=1;
   for(i=0;i<d;i++)
   {
      c =(char *)(adr+i);
      SetAPen(rp,1);
      Move(rp,93+(i<<3),101);
      Text(rp,c,1);
   }
}

showbam()
{
   int la;
   short cyl,head,sec;

   SetAPen(rp,0);
   RectFill(rp,11,121,312,190);
   getbam();
   for(i=0;i<counter;i++)
   {
      cyl=(blocks[i]>>5);
      head=((blocks[i]>>4)&1);
      sec=(blocks[i] & 15);
      SetAPen(rp,0);
      RectFill(rp,11,121,312,130);
      for(la=0;la<8;la++)
      {
         ScrollRaster(rp,0,1,11,121,312,190);
         wait(1000);
      }
      SetAPen(rp,1);
      sprintf(buf,"CYLINDER: %2d  HEAD: %2d  SECTOR: %2d",cyl,head,sec);
      place(11,180,buf);
      wait(500);
      while(key()==0x3b || key()==0x7f);
      if(key()==0x75) i=counter+2;
   }
   while(key()!=0x7f);
   menus();
}

copyme(nr)
short nr;
{
   short cb=0,cyl,head,sec,op;
   long adress,mem,old,next,first;
   int v1;

   ch1=ch2=0;
   cy2=he2=se2=ch2=0;
   adress=wbuf;

   give(0);
   if(taste(0x7f)==1) return(1);

   if(nr) getbam(); /* lesen der Bam von der Diskette        */
   else             /* simulieren der Bam fuer Cylinder 0-79 */
   {
      counter=0;
      for(cyl=0;cyl<80;cyl++)
         for(head=0;head<2;head++)
           for(sec=0;sec<11;sec++)
            {
               v1=(((v1 & 0) | cyl)<<1);
               v1=((v1 | head)<<4);
               v1=(v1 | sec);
               blocks[counter++]=v1;
            }
   }

   while(1)
   {
      if(cb) { give(0); if(taste(0x7f)==1) return(1); }

      cb=1;
      memory();
      first=old=AllocMem((long)0x00000408,MEMF_PUBLIC);

      doit(0,1);  /* Motor an */
      while(1)
      {
         cyl=(blocks[ch1]>>5);
         head=((blocks[ch1]>>4)&1);
         sec=(blocks[ch1] & 15);
         sprintf(buf,"%2d  HEAD %2d",cyl,head);
         place(115,134,buf);

         rwc(0,cyl,sec,head,&diskbuffer[0]);
         if(ver) bfsize=comp(&diskbuffer[0],old+8,0x200);
         else
         {
            *(long *) 0x3400=&diskbuffer[0];
            *(long *) 0x3404=(old+8);
            transfer();
            bfsize=0x208;
         }
         *(long *) (old+4) = bfsize;
         bfsize=(((bfsize>>3)<<3)+8);
         FreeMem(old+8+bfsize,0x400-bfsize);
         memory();
         next=AllocMem((long)0x00000408,MEMF_PUBLIC);
         ch1++;
         if(next==0||ch1>=counter)
         {
            *(long *)old=0xccddeeff;
            if(ch1>=counter) FreeMem(next,0x408);
            break;
         }
         *(long *) old = next;
         old=next;
      }

      doit(0,0);
      ch1old=ch1;
      ch2old=ch2;
      cylold=cy2;
      heold=he2;
      seold=se2;

AGAIN:
      ch1=ch1old;
      ch2=ch2old;
      cy2=cylold;
      he2=heold;
      se2=seold;
      give(1);
      if(taste(0x7f)==1) { clearmem(old,first,next); menus(); return(); }

      old = first;
      doit(0,1);
      op=createbuff(old);

      if((ch1>=counter) && cy2<80)
      {
         clear();
         while(1)
         {
            sprintf(buf,"%2d  HEAD %2d",cy2,he2);
            place(115,147,buf);
            rwc(1,cy2,0,he2,adress);
            he2^=1;
            if(he2==0) cy2++;
            if(cy2==80) break;
         }
      }

      doit(0,0);
      if(mul)
      {
         give(2);
         while(2)
         {
            v1=key();
            if(v1==0x93) break;
            if(v1==0xd5) goto AGAIN;
            if(v1==0x75) { clearmem(old,first,next); menus(); return(); }
         }
      }
      if(ch1>=counter) op=1;
      clearmem(old,first,next);
      if(op)
      {
         place(11,171,"     SUCCESSFUL BACKUP !!   (CTRL)   ");
         while(key()!=0x39);
         menus();
         return();
      }
   }
}

clearmem(old,first,next)
long old,first,next;
{
      old = first;
      while(1)
      {
         next = *(long *) old;
         bfsize= *(long *)(old+4);
         bfsize=(((bfsize>>3)<<3)+8);
         FreeMem(old,bfsize+8);
         if(next==0xccddeeff) break;
         old=next;
      }
      memory();
}

createbuff(old)
long old;
{
   long next,ad1,adress;
   short cyl,head,sec;
   short u;

   adress=wbuf;
   next=0;
   clear();

   while(1)
   {
      u=1;
      cyl=(blocks[ch2]>>5);
      head=((blocks[ch2]>>4)&1);
      sec=(blocks[ch2] & 15);

      while(u)
      {
         if(cy2==cyl && he2==head && se2==sec)
         {
            ad1=se2*0x200;
            next = *(long *) old;

            if(ver)
            {
               bfsize= *(long *)(old+4);
               decomp(old+8,wbuf+ad1,bfsize,0x200);
            }
            else
            {
               *(long *) 0x3400 = (old+8);
               *(long *) 0x3404 = (wbuf+ad1);
               transfer();
            }

            old=next;
            u=0;
            ch2++;
         }
         se2++;
         if(se2==11)
         {
            se2=ad1=0;
            sprintf(buf,"%2d  HEAD %2d",cy2,he2);
            place(115,147,buf);
            rwc(1,cy2,0,he2,adress);
            clear();
            he2++;
            if(he2==2) { he2=0; cy2++; if(cy2==80) return(1); }
         }
         if(ch2>=ch1 || old==0xccddeeff)
         {
            if(cy2>=80) return(1);
            se2=0;
            while(1)
            {
               cyl=(blocks[ch2]>>5);
               head=((blocks[ch2]>>4)&1);
               sec=(blocks[ch2] & 15);
               if(sec>=se2 && head==he2 && cyl==cy2) ch2--;
               else break;
            }
            ch2++;
            ch1=ch2;
            return(0);
         }
      }
   }
}


clear()
{
   *(long *) 0x3400 = wbuf;
   fillbuf();
}

memory()
{
   long memo;
   memo=AvailMem(0);
   sprintf(buf,"%7d",memo);
   place(83,160,buf);
}

menus()
{
   SetAPen(rp,0);
   RectFill(rp,0,0,319,200);

   SetAPen(rp,1);
   box(0,0,319,199);
   box(6,6,313,193);
   box(77,10,243,41);
   box(9,52,150,87);
   box(150,52,310,87);
   box(9,92,310,105);
   box(9,107,310,120);
   place(121,17,"BAMCOPY V1");
   place(84,28,"WRITTEN BY O.CZESLA");
   place(16,56,"F1 - BAMCOPY");
   place(16,66,"F2 - NORMALCOPY");
   place(16,76,"F3 - SHOW BAM");
   place(163,56,"F4  MULTIPLE:");
   place(163,66,"F5  COMPACTOR:");
   place(163,76,"F9  SYSTEM RESET");
   place(13,95,"DISKNAME:");
   place(13,110,"DISKSTATUS:");
   place(11,134,"READING: CYL __  HEAD __");
   place(11,147,"WRITING: CYL __  HEAD __");
   place(11,159,"MEMORY:");
   place(11,171,"COMMAND:");
   place(11,184,"     © Copyright by KICKSTART");
   memory();
   state();
}

main()
{
   short cyl,head,sec,v,m;

   openstuff();
   closescreens();
   proc=(struct Process *)FindTask("File System");
   proc->pr_WindowPtr=-1;
   menus();

   while(1)
   {
      v=key();
      if(v==0x5b) showbam();
      if(v==0x5f) copyme(1);
      if(v==0x5d) copyme(0);
      if(v==0x59)            /* multiple */
      {
         mul^=1;
         state();
         wait(50000);
      }

      if(v==0x57)            /* compact */
      {                      
         ver^=1;
         state();
         wait(50000);
      }

      if(v==0x4f) reset();
   }
}

#asm
          public _key, _fillbuf, _transfer, _reset
_key  moveq  #0,d0
      move.b $bfec01,d0
      rts

_fillbuf
      move.w #$57f,d0
      move.l $3400,a0
l1    clr.l  (a0)+
      dbf    d0,l1
      rts

_transfer
      move.w #127,d0
      move.l $3400,a0
      move.l $3404,a1
l2    move.l (a0)+,(a1)+
      dbf    d0,l2
      rts
_reset
      move.l  #$fc0000,$20 ; Reset
      rts
#endasm

give(nr)
short nr;
{
   if(nr==0) place(83,171,"INSERT SOURCE           ");
   else if(nr==1) place(83,171,"INSERT DESTINATION      ");
        else place(83,171,"WRITE BUFFER AGAIN (Y/N)");
}

wait(olcz)
long olcz;
{
   long czol;
   for(czol=0;czol<olcz;czol++);
}

openstuff()
{
   int a,b,c,d,i;

   if(!(IntuitionBase=(struct IntuitionBase *)OpenLibrary("intuition.library",0))) exit(1);
   if(!(GfxBase=(struct GfxBase *)OpenLibrary("graphics.library",0))) exit(2);
   if(!(Screen = (struct Screen *)OpenScreen(&MyScreen))) exit(3);
   diskbuffer=AllocMem (BLOCKSIZE, MEMF_CLEAR | MEMF_CHIP);
   wbuf=AllocMem (BSIZE,MEMF_CLEAR|MEMF_CHIP);
   if(!(cl=AllocMem(sizeof(struct UCopList),MEMF_PUBLIC|MEMF_CHIP|MEMF_CLEAR))) exit();

   rp= &Screen->RastPort;
   vp= &Screen->ViewPort;

   CWAIT(cl,0,0);            /* create the copperlist */
   CMOVE(cl,(*((UWORD *)0xdff140)),0); /* no pointer */
   CMOVE(cl,(*((UWORD *)0xdff142)),0);
   d=b=a=c=0;

   for(i=16;i<200;i+=16)               /* colors */
   {
      CMOVE(cl,(*((UWORD *)0xdff182)),cola[b]);
      if(d==0)
      {
         CMOVE(cl,(*((UWORD *)0xdff180)),colb[a]);
         a++;
         if(a==3) a=0;
      }

      CWAIT(cl,i,0);
      c++;
      d++;
      if(d==2) d=0;
      if(c<7) b++;
      if(c>=7 && c<=16) b--;
   }

   CWAIT(cl,200,0);
   CMOVE(cl,(*((UWORD *)0xdff180)),0x0000);
   CMOVE(cl,(*((UWORD *)0xdff182)),0x0000);
   CEND(cl);
   vp->UCopIns=cl;
   RethinkDisplay();
   SetDrMd(rp,JAM2);
   diskport=CreatePort(0,0);
   diskreq=(struct IOExtTD *)CreateExtIO(diskport,sizeof(struct IOExtTD));
   error = OpenDevice(TD_NAME,0,diskreq,0);
   doit(1,0);   /*  diskinterrupt  s p e r r e n  */
}

state()
{
   if(mul)  place(283,56,"ON ");
   else place(283,56,"OFF");
   if(ver)  place(283,66,"ON ");
   else place(283,66,"OFF");
}

displayerr(e)
short e;
{
   sprintf(buf,"DISKERROR: #%02ld     ",e);
   place(109,110,buf);
}

taste(ch)
int ch;
{
   int o;
   while(1)
   {
      o=key();
      if(o==ch)
      {
         place(83,171,">>> PLEASE WAIT <<<     ");
         return(0);
      }
      if(o==0x75) { menus(); return(1); }
   }
}



/*************************************************/
/* ELECTRONIC ARTS COMPACT AND DECOMPACT ROUTINE */
/*       BY STEVE SHAW AND JERRY MORRISON        */
/*************************************************/

#define GetByte()     (*source++)
#define PutByte(c)    { *dest++=(c); ++putSize; }
#define OutDump(nn)   dest = PutDump(dest,nn)
#define OutRun(nn,cc) dest = PutRun(dest,nn,cc)
#define MinRun        3
#define MaxRun        128
#define MaxDat        128
#define DUMP          0
#define RUN           1

LONG putSize;
char cbuf[256];

BYTE *PutDump(dest, nn)
register BYTE *dest;
register long nn;
{
   register long i;
   PutByte(nn-1);
   for(i=0;i<nn;i++)
   PutByte(cbuf[i]);
   return(dest);
}

BYTE *PutRun(dest,nn,cc)
register BYTE *dest;
register long nn,cc;
{
   PutByte(-(nn-1));
   PutByte(cc);
   return(dest);
}

comp(pSource,pDest,rowSize)
BYTE *pSource,*pDest;
LONG rowSize;
{
register    BYTE  *source,*dest;
register    char  c,lastc='\0';
BOOL        mode=DUMP;
register    short nbuf=0;
register    short rstart=0;

   source=pSource;
   dest=pDest;
   putSize=0;
   cbuf[0]=lastc=c=GetByte();
   nbuf=1;
   rowSize--;

   for (;rowSize;--rowSize)
   {
      cbuf[nbuf++]=c=GetByte();
      switch (mode)
      {
         case DUMP:
                     if(nbuf>MaxDat)
                     {
                        OutDump(nbuf-1);
                        cbuf[0]=c;
                        nbuf=1;
                        rstart=0;
                        break;
                     }
                     if(c==lastc)
                     {
                        if (nbuf-rstart>=MinRun)
                        {
                           if (rstart>0) OutDump(rstart);
                           mode = RUN;
                        }
                        else if(rstart==0) mode=RUN;
                     }
                     else rstart=nbuf-1;
                     break;
         case RUN:
                     if((c!=lastc)||(nbuf-rstart>MaxRun))
                     {
                        OutRun(nbuf-1-rstart,lastc);
                        cbuf[0]=c;
                        nbuf=1;
                        rstart=0;
                        mode=DUMP;
                     }
                     break;
      }
      lastc=c;
   }  /* ende der for schleife */

   switch(mode)
   {
      case DUMP: OutDump(nbuf); break;
      case RUN:  OutRun(nbuf-rstart,lastc); break;
   }
   return(putSize);
}


#define UGetByte()    (*source++)
#define UPutByte(c)   (*dest++ = (c))

decomp(pSource, pDest, srcBytes0, dstBytes0)
BYTE *pSource, *pDest;
WORD srcBytes0, dstBytes0;
{
   register BYTE *source = pSource;
   register BYTE *dest   = pDest;
   register WORD n;
   register BYTE c;
   register WORD srcBytes = srcBytes0,
                 dstBytes = dstBytes0;
   BOOL error = TRUE;

   while(dstBytes>0)
   {
      if((srcBytes-=1)<0)  return(error);
      n=UGetByte();
      if(n>=0)
      {
         n+=1;
         if(((srcBytes-=n)<0)||((dstBytes-=n)<0)) return(error);
         do {  UPutByte(UGetByte()); } while (--n>0);
      }

      else if(n!=-128)
      {
         n=-n+1;
         if(((srcBytes-=1)<0)||((dstBytes-=n)<0)) return(error);
         c = UGetByte();
         do {  UPutByte(c);  } while (--n>0);
      }
   }
   error=FALSE;
   return(error);
}

