#include <stdio.h>

#define SECSIZE 256       /* Change this to handle 1050 dbl density disks */

#if SECSIZE == 256
#define SECCODE 1
#define SECTORS 18
#else
#define SECCODE 0
#define SECTORS 26
#endif

int ls_file();
int cat_file();
int dup_file();
char *dmainit();
FILE *fopen(), *bopen();

struct dta {
   char reserved[21];
   char attr;
   int utime;
   int udate;
   long len;
   char name[13];
   char dummy[3];
   } fdata;

int drive, cmd;
int first;
int save[5];
char seq[27] = {6,12,18,5,11,17,4,10,16,3,9,15,2,8,14,1,7,13,0xFF};
int bitmask[8] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
unsigned char *bp;
int *seccnt;
int tracks;
int track_offset, sector_offset;

unsigned char buf[1024];
unsigned char map[1280];
unsigned char dirbuf[1024];
char dirname[100];

unsigned int bpp = 0;
int bin_flag = 0;
unsigned char dosbuf[10240];

main(argc, argv)
   int argc;
   char *argv[];
   {
   int sec, ct;
   char *p, *argp;

   
   bp = dmainit(buf);
#ifdef DEBUG
   if (bp != buf)
      printf("Buffer base moved from %x to %x\n", buf, bp);
#endif
   seccnt = (int *) &map[3];
   for (ct=0; ct<1280; ct++)
      map[ct] = 0;
   first = '\t';
   dirname[0] = '\0';
   drive = 0;
   track_offset = -40;
   tracks = 40;
   sector_offset = -1;
   while (--argc >= 0) {
      argp = *++argv;
      if ((argp[0] == '/' || argp[0] == '-')) {
         if (argp[2] == '\0') {
            if (argp[1] == 'B' || argp[1] == 'b')
               bin_flag = 1;
            else
               cmd = argp[1] & 0xDF;
            }
         else if (argp[1] == 'T' || argp[1] == 't') {
            track_offset = - atoi(&argp[2]);
            tracks = (track_offset > -60)? 40: 80;
            }
         else if (argp[1] == 'P' || argp[1] == 'p') {
            track_offset = atoi(&argp[2]);
            tracks = (track_offset < 60)? 40: 80;
            }
         else if (argp[1] == 'S' || argp[1] == 's') {
            sector_offset = atoi(&argp[2]);
            }
         }
      else if (argp[1] == ':' && argp[2] == '\0') {
         drive = (argp[0]-'A') & 0x1F;
         break;
         }
      else if (argp[1] == '\0') {
         cmd = argp[0] & 0xDF;
         break;
         }
      else {
         argv--;
         argp++;
         break;
         }
      }
#ifdef DEBUG
   printf ("Binary flag = %s\n", bin_flag? "on": "off");
   printf ("Tracks = %d, offset = %d, sector offset = %d\n",
         tracks, track_offset, sector_offset);
   printf("CMD=%c, DRIVE=%c:\n", cmd, drive+'A');
#endif
   savefb(SECCODE,SECTORS,save);
#ifdef DEBUG
   printf("SAVE=0x%04X.0x%04X.0x%04X.0x%04X.0x%04X\n", save[0], save[1],
         save[2], save[3], save[4]);
#endif
   switch (cmd) {
      case 'W':			/* Write directories or files(from MSDOS) */
         getmap(drive);
         msdir(argc,&argv[1]);
         putmap(drive);
         break;
      case 'I':			/* Initialize (Format) an Atari Disk */
         fmtdisk(drive);
         break;
      case 'L':			/* List an Atari Directory */
         listdir(361, ls_file);
         break;
      case 'P':			/* Display/Print Atari (printable) files */
         listdir(361, cat_file);
         break;
      case 'R':			/* Read Atari formatted disk files(to MSDOS)*/
         listdir(361, dup_file);
         break;
      default:
         printf ("Invalid switch: only L/istDir, P/rintFiles, R/eadFiles,\n");
         printf ("    W/riteFiles or I/nitializeDisk\n");
         printf ("Usage: util /[L|P|R|I] [/T80] [A|B|C|D]:      or\n");
         printf ("       util /W [A|B|C|D]: file [file ... ]\n");
      }
   restfb(save);
   }

dump()
   {
   int ct;
   char *p;

   for (p=bp, ct=0; ct != 256; p += 16, ct += 16) {
      printf ("%03X %02X%02X %02X%02X %02X%02X %02X%02X",
            ct, p[0]&0xFF, p[1]&0xFF, p[2]&0xFF, p[3]&0xFF,
            p[4]&0xFF, p[5]&0xFF, p[6]&0xFF, p[7]&0xFF);
      printf (" %02X%02X %02X%02X %02X%02X %02X%02X\n",
            p[8]&0xFF, p[9]&0xFF, p[10]&0xFF, p[11]&0xFF,
            p[12]&0xFF, p[13]&0xFF, p[14]&0xFF, p[15]&0xFF);
      }
   }

listdir(sec, filefunc)
   int sec;
   int (*filefunc)();
   {
   int k, j, i, *num;
   unsigned char *p, *tp, *dp;
   char text[30];

   printf("Files in \"%c:%s\"\n", drive+'A', dirname);
   for (j=0; j<1024; sec++) {
      if (aread(sec, drive, bp)) {
         printf("Failure reading directory!  Operation aborted!\n");
         return;
         }
#ifdef TRACE
      dump();
#endif
      for (i=0; i < 128;)
         dirbuf[j++] = bp[i++];
      }
#ifdef DEBUG
   printf("Directory read:\n");
#endif
   for (p=dirbuf; p < dirbuf+1024; p+=16) {
      if (*p == 0) {
#ifdef DEBUG
         printf("End of directory at byte %d\n", p-dirbuf);
#endif
         break;
         }
#ifndef DEBUG
      if ((*p & 0x91) != 0x00)
         continue;
#endif
      for (tp=text, dp=p+5; *dp > ' ' && dp != p+13;)
         *tp++ = *dp++;
      if (p[13] > ' ') {
         *tp++ = '.';
         *tp++ = p[13];
         if (p[14] > ' ') {
            *tp++ = p[14];
            if (p[15] > ' ')
               *tp++ = p[15];
            }
         }
      *tp = '\0';
      num = (int *) (p+1);
      (*filefunc)(text, num[1], num[0], *p);
      }
   if (first == '\n') {
      putchar('\n');
      first = '\t';
      }
   for (p=dirbuf; p < dirbuf+1024; p+=16) {
      if (*p == 0)
         return;
      if ((*p & 0x91) != 0x10)
         continue;
      for (tp=text, dp=p+5; *dp > ' ' && dp != p+13;)
         *tp++ = *dp++;
      if (p[13] > ' ') {
         *tp++ = '.';
         *tp++ = p[13];
         if (p[14] > ' ') {
            *tp++ = p[14];
            if (p[15] > ' ')
               *tp++ = p[15];
            }
         }
      *tp = '\0';
#ifdef DEBUG
      printf("Subdirectory :%s!\n", text);
#endif
      num = (int *) (p+1);
      k = strlen(dirname);
      dirname[k] = '\\';
      dirname[k+1] = '\0';
      strcat(dirname, text);
      if (cmd = 'D')
         if (cd(text) != 0)
            if (md(text) != 0) {
               printf("Cannot use or create directory \"%s\"\n", text);
               return;
               }
      listdir(num[1], filefunc);
      if (cmd == 'D')
         cd("..");
      dirname[k] = '\0';
      }
   }

ls_file(name, at, size, flags)
   char *name;
   int at, size, flags;
   {
   printf ("%c%c%c%-12s[%04X] %d secs%c",
         flags&0x20?'*':' ', flags&0x10?':':' ',
         flags&0x04?'!':flags&0x02?' ':'#',
         name, at, size, first);
   first ^= '\n' ^ '\t';
   }

cat_file(name, at, size, flags)
   char *name;
   int at, size, flags;
   {
   int x, ct;
   unsigned char *p;

   printf("Print %s(%ld bytes, f=%02X)?", name, size*((long)SECSIZE-3L),
         flags);
   x = getchar() & 0xDF;
   if (x != '\n')
      while (getchar() != '\n')
         ;
   if (x == 'Y') {
      for (x = at; x != 0; x = ((bp[SECSIZE-3]<<8)+bp[SECSIZE-2]) & 0x03FF) {
         while (aread(x, drive, bp))
            printf ("5 Failures reading sector on Atari disk\n");
         for (ct=bp[SECSIZE-1], p=bp; ct > 0; ct--, p++) {
            if (*p == 0x7F)
               putchar('\t');
            else if (*p == 0x9B)
               putchar('\n');
            else
               putchar(*p);
            }
         }
      }
   }

dup_file(name, at, size, flags)
   char *name;
   int at, size, flags;
   {
   int x, ct, mask;
   char binary_copy;
   unsigned char *p;
   FILE *outf;

   binary_copy = bin_flag;
   printf("Copy %s(%ld bytes, f=%02X)?", name, size*((long)SECSIZE-3L),
         flags);
   mask = (flags & 0x04)? 0xFF: 0x03;
   x = getchar() & 0xDF;
   if (x != '\n')
      while (getchar() != '\n')
         ;
   if (x == 'Y' || x == 'A' || x == 'B') {
      if (x == 'A')
         binary_copy = 0;
      else if (x == 'B')
         binary_copy = 1;
      outf = bopen(name, "wb");
      if (outf == NULL) {
         printf ("Cannot create file \"%s\"", name);
         return;
         }
      for (x = at; x != 0;
	    x = ((bp[SECSIZE-3]&mask) << 8)+(bp[SECSIZE-2] & 0xFF)) {
         while (aread(x, drive, bp))
            printf ("5 Failures reading sector on Atari disk\n");
#ifdef DEBUG
         printf ("Link data = %02x, %02x, %02x\n", bp[SECSIZE-3],
               bp[SECSIZE-2], bp[SECSIZE-1]);
#endif
         for (ct=bp[SECSIZE-1], p=bp; ct > 0; ct--, p++) {
#ifdef TRACE
            putchar(*p);
#endif
            if (binary_copy)
               bput(*p, outf);
            else if (*p == 0x7F)
               bput('\t', outf);
            else if (*p == 0x9B) {
               bput('\r', outf);
               bput('\n', outf);
               }
            else
               bput(*p, outf);
            }
         }
      bclose(outf);
      }
   }

getmap(drive)
   int drive;
   {
   int p, n, k;
   char *mp;

   for (mp = map, n=0; n++ < sizeof(map);)
      *mp++ = 0;
   mp = map;
   n = 360;
   p = 3;
   do {
      if (aread (n--, drive, bp)) {
         printf ("Unable to read the Atari disk VTOC/Bit Map!  Program aborted!\n");
         exit(1);
         }
      for (k=0; k<256; k++)
         *mp++ = bp[k];
      if (map[0] > (sizeof(map)/256 + 2)) {
         printf ("Invalid drive, more than %d sectors\n",
               (sizeof(map) - 10)*8);
         exit(1);
         }
      } while (++p <= map[0]);
#ifdef DEBUG
   printf ("Drive %c: map header: <%02X> %02x%02x %02x%02x ", drive+'A',
         map[0], map[2], map[1], map[4], map[3]);
   printf ("%02x %02x %02x %02x %02x\n", map[5], map[6], map[7], map[8],
         map[9]);
#endif
   }

putmap(drive)
   int drive;
   {
   int p, n, k;
   char *mp;

   mp = map;
   n = 360;
   p = map[0];
   do {
      for (k=0; k<256; k++)
         bp[k] = *mp++;
      awrite (n--, drive, bp);
      } while (--p >= 3);
#ifdef DEBUG
   printf ("Drive %c: map header: <%02X> %02x%02x %02x%02x ", drive+'A',
         map[0], map[2], map[1], map[4], map[3]);
   printf ("%02x %02x %02x %02x %02x\n", map[5], map[6], map[7], map[8],
         map[9]);
#endif
   }

msdir(argc,argv)
   int argc;
   char *argv[];
   {
   int rc, k, sc, fno, bk, bk0, bkn;
   FILE *inf;

   restfb(save);
   bkn = -1;
   while (--argc >= 0) {
#ifdef DEBUG
      printf ("Argument <<%s>>\n", *argv);
#endif
      if ((rc = ffirst(&fdata, *argv)) == 0) {
         do {
            printf ("Copying <<%s>>, Length = %ld\n", fdata.name, fdata.len);
            inf = fopen(fdata.name, (bin_flag? "rb": "r"));
            savefb(SECCODE,SECTORS,save);
            fno = dirfnd(fdata.name);
            bk0 = bk = alloc();
#ifdef DEBUG
            printf ("   First block = %d (FNO=%d)\n", bk, fno);
#endif
            k = (fno<<4) & 0x0070;
            bp[k+3] = bk;
            bp[k+4] = bk>>8;
            k = (fno>>3) + 361;
#ifdef DEBUG
            printf("   Writing dir bk. %d, drive %c:\n", k, drive+'A');
#endif
            awrite(k, drive, bp);
            restfb(save);
            sc = 1;
            fno <<= 2;
            k = 0;
            while ((rc = fgetc(inf)) >= 0) {
               if (rc == '\t')
                  rc = 0x7F;
               else if (rc == '\n')
                  rc = 0x9B;

               if (k >= SECSIZE-3) {
                  bkn = alloc();
                  if (bkn <= 0) {
                     printf ("No more sectors available\n");
                     break;
                     }
#ifdef DEBUG
                  printf ("   Writing block %d (fno=%04X,link=%d)\n", bk,
                        fno, bkn);
#endif
                  bp[SECSIZE-3] = fno + (bkn>>8);
                  bp[SECSIZE-2] = bkn & 0xFF;
                  bp[SECSIZE-1] = SECSIZE-3;
                  savefb(SECCODE,SECTORS,save);
                  awrite(bk, drive, bp);
                  restfb(save);
                  bk = bkn;
                  k = 0;
                  sc++;
                  }
               bp[k++] = rc;
               }
            fclose(inf);
            bp[SECSIZE-3] = fno;
            bp[SECSIZE-2] = 0;
            bp[SECSIZE-1] = k;
#ifdef DEBUG
            printf ("   Writing last block %d (fno=%04X,size=%d)\n", bk,
                  fno, k);
#endif
            savefb(SECCODE,SECTORS,save);
            awrite(bk, drive, bp);
            bk = bk0;
            dirup(sc, 0x42, fno>>2);
            restfb(save);
            } while (fnext(&fdata) == 0 && bkn != 0);
         }
      else
         printf ("%s <<%s>>\n",
               rc<18? "Invalid path for": "No matching file", *argv);
      argv++;
      }
   savefb(SECCODE,SECTORS,save);
   }

dirfnd(name)
   char *name;
   {
   char *name0, *cp;
   char namev[12];
   int j, k, first;

   k = 0;
   for (j=0; j<11; j++)
      namev[j] = ' ';
   namev[11] = '\0';
   while (*name != '.' && *name != '\0') {
      namev[k++] = *name++;
      if (k >= 11)
         break;
      }
   if (k < 11 && *name == '.') {
      k = 8;
      while (*++name != '\0' && k < 11) {
         if (*name == '\n')
            break;
         namev[k++] = *name;
         }
      }
   first = -1;
   for (k=361; k<369; k++) {
      if (aread(k, drive, bp)) {
         printf ("Unable to read ROOT directory for updating! Program aborted!\n");
         exit(1);
         }
      for (j=0; j<0x80; j += 0x10) {
         if (namecomp(&bp[j+5], namev)) {
            first = ((k-361)<<3) + (j>>4);
#ifdef DEBUG
            printf("   Replaced file no. = %d\n", first);
#endif
            return (first);
            }
         if (bp[j] == 0) {
            if (first < 0)
               first = ((k-361)<<3) + (j>>4);
            goto allcf;
            }
         if ((bp[j] & 0x80) && first < 0)
            first = ((k-361)<<3) + (j>>4);
         }
      }
   allcf:
#ifdef DEBUG
   printf ("allocated file no. = %d\n", first);
#endif
   if (first < 0) {
      printf ("No directory entries free\n");
      exit(1);
      }
   if ((first>>3) != (k-361))
      while (aread((first>>3)+361, drive, bp))
         ;
   cp = &bp[(first & 0x07)<<4];
   *cp = 0x03;
   cp += 5;
   for (k=0; k<11; k++)
      *cp++ = namev[k];
#ifdef DEBUG
   printf ("%s==>%s(FNO=%d)\n", name0, namev, first);
#endif
   return (first);
   }

dirup(cnt, attr, indx)
   int cnt, attr, indx;
   {
   int k;

   while (aread(361+(indx>>3), drive, bp))
      ;
   k = (indx<<4) & 0x0070;
   bp[k] = attr;
   *(int *)(&bp[k+1]) = cnt;
   awrite(361+(indx>>3), drive, bp);
   }

alloc()
   {
   int p, x, mask;

   for (x=10; map[x] == 0; x++)
      if (x >= 1280)
         return(0);
   p = (x-10)<<3;
   mask = 0x80;
   while ((map[x] & mask) == 0) {
      mask >>= 1;
      p++;
      }
   map[x] &= ~mask;
   --*seccnt;
   return (p);
   }

dealloc(sec)
   int sec;
   {
   map[(sec >> 3) + 10] |= bitmask[sec & 0x07];
   }

namecomp(a, b)
   char *a, *b;
   {
   int i;

   for (i=0; i<11; i++)
      if (*a++ != *b++)
         return(0);
   return (1);
   }

FILE *bopen(name, mode)
   char *name, *mode;
   {
   FILE *val;

   restfb(save);
   val = fopen(name, mode);
   savefb(SECCODE,SECTORS,save);
   return (val);
   }

bput(data, out)
   int data;
   FILE *out;
   {
   int len;

   if (bpp >= sizeof(dosbuf)) {
      restfb(save);
      len = fwrite (dosbuf, 1, sizeof(dosbuf), out);
      if (len != sizeof(dosbuf))
         printf ("Error writing to DOS file, %d bytes not written\n",
               sizeof(dosbuf) - len);
#ifdef DEBUG
      printf("Writing block of %d bytes to DOS file\n", sizeof(dosbuf));
#endif
      bpp = 0;
      savefb(SECCODE,SECTORS,save);
      }
   dosbuf[bpp++] = data;
   }

bclose(out)
   FILE *out;
   {
   int len;

   restfb(save);
   if (bpp != 0) {
      len = fwrite(dosbuf, 1, bpp, out);
      if (len != bpp)
         printf ("Error closing file, %d bytes not written\n", bpp - len);
      }
#ifdef DEBUG
   printf ("Last file block contains %d bytes\n", bpp);
#endif
   bpp = 0;
   fclose(out);
   savefb(SECCODE,SECTORS,save);
   }

fmtdisk(drive)
   int drive;
   {
   int ct;

   for (ct=0; ct<40; ct++) {
      if (aformat(ct, drive, bp, seq) < 0)
      printf ("Error formatting track %d\n", ct);
      }
   for (ct=0; ct<512; ct++)
      map[ct] = 0;
   map[0] = 2;
   map[1] = map[3] = 0xc4;
   map[2] = map[4] = 0x02;
   for (ct=4; ct<360; ct++)
      dealloc(ct);
   for (ct=369; ct<720; ct++)
      dealloc(ct);
   for (ct=0; ct<1024; ct++)
      dirbuf[ct] = 0;
   putmap(drive);
   for (ct=361; ct < 369; ct++)
      awrite(ct, drive, dirbuf);
   }

