/* DECtape image scanner (revised Nov 2004 for AA2 format) */

#include <stdio.h>
#include <string.h>

#define psym(f, k) fputc((k),f)

static int *B;

void shit (char *s, int n) {fprintf(stderr,"**SHIT(%o): %s\n",n,s);}
void die (char *s, int n) {shit(s,n); exit(1);}

int mangle (int n) {
int i,m;
  n = ~n;
  m = (n&7)<<15;
  m += (n&070)<<9;
  m += (n&0700)<<3;
  m += (n&07000)>>3;
  m += (n&070000)>>9;
  return m+((n&0700000)>>15);
}

void mangleblock (int block) {
int min,max,val;
  min = block*256; max = min+255;
/*fprintf(stderr,"Mangling block %o %o:%o %o %o,",*/
/*block,min,max,B[min],B[max]);*/
  while (min<max) {
    val = B[min]; B[min++]=mangle(B[max]); B[max--]=mangle(val);
  }
/*min=block*256;max=min+255;*/
/*fprintf(stderr," %o %o\n",B[min],B[max]);*/
}

void sixbt (char **sp, int n) {
#define s (*sp)
int i,j;
  for (i=12; i>=0; i-=6) {
    j = (n>>i)&63; if (j) {if ((j&32)==0) j+=64; *s++ = j;}
  }
#undef s
}

void aa1 (FILE *f, int n) {
int i,j,k;
  for (i=12; i>=0; i-=6) {
    j = (n>>i)&63;
    if (j) {
      if ((j&32)==0) j+=64;
      if (j=='"') j = '\n';
      if (j=='^') j = '@';
      psym(f, j);
    }
  }
}

void dec1 (FILE *f, int n) {
  n = n&127;
  if (n=='\r') n = '\n';
  if (n) psym(f, n);
}

void dec5 (FILE *f, int a, int b) {
  dec1(f, a>>11); dec1(f, a>>4);
  dec1(f, (a<<3)+((b>>15)&7));
  dec1(f, b>>8); dec1(f, b>>1);
}

int bitmap (int fileno) {
  if (fileno<0) die("Fileno negative",fileno);
  if (fileno==0) return 256*64;
  if (fileno>56) die("Fileno excessive",fileno);
  return 256*57+((fileno-1)*32);
}

int blocksused (int bitmap) {
int i,j,m,n;
  n=0;
  for (i=0; i<32; i++) {
    m = B[bitmap++]; j = 0400000;
    while (j) {if (m&j) n++; j >>= 1;}
  }
  return n;
}

int filedesc (int fileno) {
  if (fileno<1) die("Fileno too small",fileno);
  if (fileno>56) die ("Fileno too big",fileno);
  return 256*64+(fileno-1)*4+32;
}

char *printname (int d) {
static char temp[256];
char *s = temp;
  sixbt(&s, B[d]);
  sixbt(&s, B[d+1]);
  *s++ = ' ';
  sixbt(&s, B[d+2]);
  *s = '\0';
  return(strdup(temp));
}

void dir (int *last) {
int i,d,m,u;
  for (i=1; i<=24; i++) {
    d = filedesc(i); m = bitmap(i); u = blocksused(m);
    if (u || B[d+3]&0400000) {
      fprintf(stderr, "%02d %3d %s\n",i,u, printname(d));
    }
  }
  *last = i;
  fprintf(stderr, "%11d\n",blocksused(0));
}

void decfile (FILE *f, int start) {
int p,lim,pairs,mangled=0,next;
  while (start>0) {
/*fprintf(stderr,"//Next block %o\n",start);*/
    p = start*256; lim = p+254;
    while (p<lim) {
/*fprintf(stderr,"//Block %o Word %o %06o\n",p>>8,p&255,B[p]);*/
      if (B[p]==0) break;
      if (B[p]==01005) break;
      if ((B[p]&0777)!=2) {shit("DEC format mode not 2",p); return;}
      pairs = B[p]>>9; p += 2;
      while(--pairs>0) {
        if (p>=lim) {shit("DEC record error",p-lim); return;}
        dec5(f, B[p],B[p+1]); p += 2;
      }
    }
    next = B[lim+1];
    if (mangled) mangleblock(start);
    if (next<start) {
      mangled = 1; mangleblock(next);
    }
    start = next; if (start>=01100) start = -1;
  }
}

#define AA1 010161
#define AA2 010162

static int aa2last=1;

void aa2one (FILE *f, int k) {
  if (k) {
    if (aa2last) {psym(f,k+31);}
    else if (k>32) {psym(f,k-32);}
    else psym(f,k+94);
  }
  aa2last = k;
}

void aa2three (FILE *f, int k) {
  aa2one(f,(k>>12)&63);
  aa2one(f,(k>>6)&63);
  aa2one(f,k&63);
}

void aa2file (FILE *f, int start) {
int p,lim,sum=AA2,w,mangled=0,next;
  aa2last = 1;
  while (start>0) {
    p = start*256; lim = p+255;
/*fprintf(stderr,"Block %o %o:%o %06o %06o sum %06o\n",*/
/*start,p,lim,B[p],B[lim],sum);*/
    if (B[p++]!=(sum&0777777)) {
      shit("AA2 checksum error - remove -aa flag\?",p-1); return;
    }
    while (p<lim) {
      w = B[p++]; sum += w;
      aa2three(f,w);
    }
    next = B[lim];
    if (mangled) mangleblock(start);
    if (next<start) {
      mangled = 1; mangleblock(next);
    }
    start = next; if (start>=01100) start = -1;
  }
}

void aa1file (FILE *f, int start) {
int p,lim,sum=AA1,w,mangled=0,next;
  while (start>0) {
    p = start*256; lim = p+255;
/*fprintf(stderr,"Block %o %o:%o %06o %06o sum %06o\n",*/
/*start,p,lim,B[p],B[lim],sum);*/
    if (B[p++]!=(sum&0777777)) {
      if (sum==AA1 && B[p-1]==AA2) {aa2file(f,start); return;}
      shit("AA1 checksum error - remove -aa flag\?",p-1); return;
    }
    while (p<lim) {
      w = B[p++]; sum += w;
      aa1(f, w);
    }
    next = B[lim];
    if (mangled) mangleblock(start);
    if (next<start) {
      mangled = 1; mangleblock(next);
    }
    start = next; if (start>=01100) start = -1;
  }
}   

int main(int argc, char **argv) {
int aaflag = (0!=0);
int size=576*256*4;
int this, last, i;
FILE *tape, *outfile;
char dta[256], txt[256], *s;

  B = (int*) malloc(size);

  if (argc<2) die ("syntax: dectape [-aa] tapename",argc);
  if (strcmp(argv[1], "-aa") == 0) {
    aaflag = (0==0);
    argc -= 1; argv += 1;
  }
  mkdir(argv[1], 0777);
  sprintf(dta, "%s.dta", argv[1]);
  tape = fopen(dta,"rb");
  if (tape==0) die("No tape file",0);
  i = fread(B,1,size,tape);
  fprintf(stderr, "Got %d out of %d bytes\n\n",i,size);

  fprintf(stderr, "Directory\n\n");
  dir(&last);
  for (this = 1; this <= last; this++) {
    i = this;
    fprintf(stderr, "Going for file %d\n", i);
    i = filedesc(i);
    sprintf(txt, "%s/%s", argv[1], printname(i));
    if ((s = strchr(txt, ' ')) != NULL) *s = '.';
    s = txt + strlen(txt) - 1; if (*s == '.') *s = '\0';
    s = txt;
    while (*s != '\0') {
      if (isalpha(*s) && isupper(*s)) *s = tolower(*s);
      s += 1;
    }
    fprintf(stderr, "That's file %s", txt);
    i = B[i+3];
    fprintf(stderr, ", First block %o\n",i);
    i = i&01777;
    if (i) {
      outfile = fopen(txt, "wb");
      if (aaflag) aa1file(outfile, i); else decfile(outfile, i);
      fclose(outfile);
    }
  }
  return(0);
}
