/* converts a raw disk image file into a dos-order disk image */
/* Copyright (c) 1996 by Christopher Bachmann (c-bachmann@nwu.edu)

   Feel free to redistribute it, port it, copy it around, but if you
   change it, improve it, base something else on it, etc.  Please
   let me know, so that I can keep this up to date, and incorporate
   the best of everything.

*/

#include <stdio.h>

/*static unsigned char disk[35][16][343];	/* encoded data, checksum */
static unsigned char raw_track[16][343];
/*static unsigned char output[35][16][256];	/* decoded output disk */
static unsigned char decoded_track[16][256];
static long int start[35];		/* start positions for each track */
static long int length[35];		/* length of each track */
static int track;
static int sector;

unsigned char translation[] = {
  0x96, 0x97, 0x9A, 0x9B, 0x9D, 0x9E, 0x9F, 0xA6,
  0xA7, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB2, 0xB3,
  0xB4, 0xB5, 0xB6, 0xB7, 0xB9, 0xBA, 0xBB, 0xBC,
  0xBD, 0xBE, 0xBF, 0xCB, 0xCD, 0xCE, 0xCF, 0xD3,
  0xD6, 0xD7, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE,
  0xDF, 0xE5, 0xE6, 0xE7, 0xE9, 0xEA, 0xEB, 0xEC,
  0xED, 0xEE, 0xEF, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6,
  0xF7, 0xF9, 0xFA, 0xFB, 0xFC, 0xFD, 0xFE, 0xFF
};
unsigned char interleave[] = {
  0x00, 0x07, 0x0E, 0x06, 0x0D, 0x05, 0x0C, 0x04,
  0x0B, 0x03, 0x0A, 0x02, 0x09, 0x01, 0x08, 0x0F
};

unsigned char reverse[] = {0x00, 0x02, 0x01, 0x03};

/* open the file for reading as binary */
FILE *openfile(char *filename) {
  FILE *fileno;

  fileno = fopen(filename, "rb");
  return fileno;
}

/* read in information from the header */
void readheader (FILE *fileid) {
  int i;

  fseek (fileid, (long) 0x2b0, 0);		/* point to first data field */
  for (i=0; i < 35; i++) {
    start[i] = 0;
    start[i] = ((((long) getc (fileid)) & 0xff) << 16) | start[i];
    start[i] = ((((long) getc (fileid)) & 0xff) << 8) | start[i];
    start[i] = ((((long) getc (fileid)) & 0xff)) | start[i];
    start[i] = ((((long) getc (fileid)) & 0xff) << 24) | start[i];
/*    start[i] = ((getc(fileid) & 0xff) << 16) | ((getc(fileid) & 0xff) << 8) |
      ((getc(fileid) & 0xff)) | ((getc(fileid) & 0xff) << 24); */
/*    printf ("%x ",start[i]); */
  }
  printf ("\n");
  fseek (fileid, (long) 0x440, 0);
  for (i=0; i < 35; i++) {
    length[i] = 0;
    length[i] = ((((long) getc (fileid)) & 0xff)) | length[i];
    length[i] = ((((long) getc (fileid)) & 0xff) << 8) | length[i];
    length[i] = ((((long) getc (fileid)) & 0xff) << 16) | length[i];
    length[i] = ((((long) getc (fileid)) & 0xff) << 24) | length[i];
/*    length[i] = ((getc(fileid) & 0xff)) | ((getc(fileid) & 0xff) << 8) |
      ((getc(fileid) & 0xff) << 16) | ((getc(fileid) & 0xff) << 24); */
/*    printf ("%x ",length[i]); */
  }
/*  printf ("\n"); */
}

/* print the short as 4 hex digits, with a line number */
void printbyte (short number) {
  int high,low;
  high = (number >> 8) & 0xff;
  low = number & 0xff;
/*
  if (high < 16)
    printf ("0%x",high);
  else
    printf ("%x",high);
*/
  if (low < 16)
    printf ("0%x ",low);
  else
    printf ("%x ",low);
}

/* print address information */
void print_address (int *address) {
  int i;
  int volume;

  volume = (address[3] << 1 & 0xff | 0x01) & (address[4] & 0xff);
  track  = (address[5] << 1 & 0xff | 0x01) & (address[6] & 0xff);
  sector = (address[7] << 1 & 0xff | 0x01) & (address[8] & 0xff);

/*  printf ("Address: V%x T%x S%x\n",volume,track,sector); */
}

/* print output data information */
void print_output () {
  int i;

  for (i=0; i < 256; i++) {
/*    printf ("%x ",output[track][interleave[sector]][i]); */
    printf ("%x ",decoded_track[interleave[sector]][i]);
  }
  printf ("\n");
}

/* print sector data information */
void print_data (unsigned char *data) {
  int i,j;

  for (i=0; i < 343; i++) {
    printf ("%x ",data[i]);
  }
  printf ("\n");
}

/* finds the address header, and reads in the current track/sector */
read_address(FILE *fileid) {
  int address[14];	/* address information */
  int done = 0;		/* flag */
  int buffer = 0xffff;	/* bit buffer (like apple latch) */
  int input_byte;	/* input byte */
  int input_bit;	/* current bit */
  int bit_count;	/* number of bits shifted in so far */
  int index=0;		/* index into address array */

  while (!done) {
    input_byte = getc(fileid);
    if (input_byte == EOF) {	/* check for end of file ?? */
      done = 1;
    } else {
      for (input_bit = 0; input_bit < 8; input_bit++) {
	/* get current bit */
	buffer = ((buffer << 1) & 0xfffe) | ((input_byte >> 7) & 1);
	input_byte = input_byte << 1;

	/* update count */
	bit_count++;

	/* test for valid data */
	if ((bit_count >= 8) && ((buffer & 0x0080) != 0)) {
/*	  printbyte (buffer); */
	  /* find prolog of address block */
	  if ((index != 0) && (index < 3)){
	    address[index++] = (buffer & 0xff);
	    if (address[1] != 0xAA) {	/* make sure address prolog */
	      index = 0;
	    }
	    if (index == 3) {
	      if (address[2] != 0x96) {	/* make sure address prolog */
		index = 0;
	      }
	    }
	  } else if (index == 0) {	/* searching for 0xD5 */
	    if ((buffer & 0xff) == 0xD5) {
	      address[index++] = 0xD5;
	    }
	  } else {			/* reading address information */
	    address[index++] = (buffer & 0xff);
	    if (index == 14) {
	      done = 1;
	      input_bit = 8;
	    }
	  }
	  bit_count = 0;		/* reset bit counter */
	  buffer = 0xffff;        /*  and buffer */
	}	/* end valid data */
      }		/* end current byte */
    }		/* end EOF test */
  }		/* end while loop */

  print_address(address);	/* display current address */
}

/* finds the next data sector, and reads the encoded version in */
find_data(FILE *fileid) {
  int prolog[3];	/* prolog information */
  int done = 0;		/* flag */
  int buffer = 0xffff;	/* bit buffer (like apple latch) */
  int input_byte;	/* input byte */
  int input_bit;	/* current bit */
  int bit_count;	/* number of bits shifted in so far */
  int index=0;		/* index into prolog array */
  int dataindex=0;	/* index into sector array */

  while (!done) {
    input_byte = getc(fileid);
    if (input_byte == EOF) {	/* check for end of file ?? */
      done = 1;
    } else {
      for (input_bit = 0; input_bit < 8; input_bit++) {
	/* get current bit */
	buffer = ((buffer << 1) & 0xfffe) | ((input_byte >> 7) & 1);
	input_byte = input_byte << 1;

	/* update count */
	bit_count++;

	/* test for valid data */
	if ((bit_count >= 8) && ((buffer & 0x0080) != 0)) {
/*	  printbyte (buffer); */
	  /* find prolog of data block */
	  if ((index != 0) && (index < 3)){
	    prolog[index++] = (buffer & 0xff);
	    if (prolog[1] != 0xAA) {	/* make sure data prolog */
	      index = 0;
	    }
	    if (index == 3) {
	      if (prolog[2] != 0xAD) {	/* make sure data prolog */
		index = 0;
	      }
	    }
	  } else if (index == 0) {	/* searching for 0xD5 */
	    if ((buffer & 0xff) == 0xD5) {
	      prolog[index++] = 0xD5;
	    }
	  } else {			/* reading data information */
/*	    disk[track][sector][dataindex++] = (buffer & 0xff); */
	    raw_track[sector][dataindex++] = (buffer & 0xff);
	    if (dataindex == 343) {
	      done = 1;
	      input_bit = 8;
	    }
	  }
	  bit_count = 0;		/* reset bit counter */
	  buffer = 0xffff;        /*  and buffer */
	}	/* end valid data */
      }		/* end current byte */
    }		/* end EOF test */
  }		/* end while loop */

/*  print_data(disk[track][sector]); */
/*  print_data(raw_track[sector]); */
}

/* decode the data and put it into the output disk */
void decode_data() {
  int i,j;
  int check;				/* checksum */
  unsigned char low[86];		/* temporary values */
  unsigned char high[256];
  unsigned char temp_output[256];	/* temporary output */

  check = 0;
  /* first translate raw bytes, and compute checksum */
  for (i=0;i<86;i++) {
    j = 0;
/*    while (translation[j] != disk[track][sector][i]) */
    while (translation[j] != raw_track[sector][i])
      j++;
    check = j^check;
    low[i] = check;
  }
  for (i=86;i<342;i++) {
    j = 0;
/*    while (translation[j] != disk[track][sector][i]) */
    while (translation[j] != raw_track[sector][i])
      j++;
    check = j^check;
    high[i-86] = check << 2;
  }
  j = 0;
/*  while (translation[j] != disk[track][sector][342]) */
  while (translation[j] != raw_track[sector][342])
    j++;
  check = check^j;

  /* checksum should equal zero */
  if (check != 0) {
    printf ("Error Track %d Sector %d: Checksum invalid = %d\n",
	    track,sector,check);
  }

  /* decode bytes and store them into the buffer */
  for (i=0; i<256; i++) {
    temp_output[i] = 0;
    if (i<86)
      temp_output[i] = (high[i] | reverse[(low[i]&0x03)]);
    else if (i<172)
      temp_output[i] = (high[i] | reverse[((low[i-86]&0x0C)>>2)]);
    else
      temp_output[i] = (high[i] | reverse[((low[i-172]&0x30)>>4)]);
  }

  /* copy the bytes into the right sector on the disk */
  for (i=0; i<256; i++) {
/*    output[track][interleave[sector]][i] = temp_output[i]; */
    decoded_track[interleave[sector]][i] = temp_output[i];
  }

/*  print_output(); */
}

/* process a single track into encoded disk */
void read_track(FILE *fileid) {
  int i;

  for (i=0; i < 16; i++) {
    read_address(fileid);	/* find the address */
    find_data(fileid);		/* find next data */
    decode_data();		/* get the decoded data */
  }
}

/* process the file as if we are a real apple reading the disk */
void process(FILE *fileid, char *filename) {
  FILE *fileno;
  int trk;		/* track */
  int j,k;		/* sector/byte */

  printf ("Processing ");
  fileno = fopen(filename, "wb");
  for (trk=0; trk < 35; trk++) {
/*    printf ("Track %d\n",trk); */
    printf (".");
    fseek (fileid, start[trk], 0);	/* seek to start of track */
    read_track(fileid);			/* read in track */
    for (j=0;j<16;j++) {
      for (k=0;k<256;k++) {
/*	fprintf (fileno,"%c",decoded_track[j][k]); */
	putc (decoded_track[j][k],fileno);
      }
    }
  }
  fclose (fileno);
  printf ("Done\n");
}

/* close the file after reading */
void closefile(FILE *fileid) {
  fclose (fileid);
}

void main(int argc, char *argv[]) {
  char *filename;
  FILE *fileid;

  if (argc < 3) {
    printf ("img2dsk <image file> <outputfile>\n");
  } else {
    fileid = openfile(argv[1]);
    if (fileid == NULL) {
      printf ("Filename not found\n");
    } else {
      readheader (fileid);
      process (fileid, argv[2]);
      closefile(fileid);
    }
  }
}
