Recovering old Infocom games from Atari 400/800 disks

From: Michael Current (mcurrent@carleton.edu)
Date: 08/27/98-09:47:41 PM Z


From: Michael Current <mcurrent@carleton.edu>
Subject: Recovering old Infocom games from Atari 400/800 disks
Date: Thu Aug 27 21:47:41 1998

From: jenkin@cs.yorku.ca (Michael Jenkin)
>Newsgroups: rec.games.int-fiction
Subject: recovering old infocom games from atari 400/800 disks
Date: 2 Apr 93 17:45:18 GMT
Organization: York University, CS Dept. Toronto
Lines: 259


The following two programs will extract infocom datafiles from atari800
(yes the 8bit machine, not the st) infocom games. The resulting datafiles
can then be played with either the infocom interpreter or with the
publicly available interpreters that can be found on the net.

To use this you will need to get the raw disk image from the original
infocom disk uploaded to a somewhat larger machine. Each side of an atari
disk has 720 sectors of 128 bytes. Upload these sectors in order into a
binary file on your favourite modern platform.
NB: This is quite a task. The fastest modem I could attach to the atari
was 1200 baud and disk/memory limits make it necessary to break a full
disk into a number of smaller files containg the sectors before downloading.

Two programs follow. zoffset works for single sided games. It takes
the games serial number, the name of a file containing the raw sectors and an
output file and does the conversion. zheader2 does the same thing for
two sided games.

The serial number can be found by either
running the game or by doing a strings -6 on the disk image and look for
something that resembles a date or by running the game on the atari
-- it is printed out first thing.


Michael Jenkin
York University

-------------- zoffset.c

/*
 * a filter to take a raw disk file from an ATARI 800 infocom game
 * (version 3 or earlier) and the serial number (obtained via strings -6)
 * and produce a data file playable with either zmachine or infocom
 *
 * This version works for games written to a single side of the disk only
 * (I've seen zork1, 2, and 3 this way as well as deadline. Later games
 * are written on both sides of the disk. Use the 2 sided utility instead.)
 *
 * Michael Jenkin, 1993
 */
# include <stdio.h>

/* the disk */
unsigned char disk[720*128];

main(argc, argv)
     int argc;
     char *argv[];
{
  int i, offset, len;
  unsigned short sum, gsum;
  FILE *fd;

  if(argc != 4){
    (void) fprintf(stderr,"usage: zoffset <serial-id> <disk> <outfile>\n");
    exit(1);
  }
  if(strlen(argv[1]) != 6){
    (void) fprintf(stderr, "serial id's are 6 bytes long e.g. 830329\n");
    exit(1);
  }

  if((fd = fopen(argv[2], "r")) == NULL){
    (void) fprintf(stderr, "open of disk file %s failed!\n",argv[2]);
    exit(1);
  }
  if(fread(disk, sizeof(disk), 1, fd) != 1){
    (void) fprintf(stderr, "read of disk file %s failed!\n",argv[2]);
    exit(1);
  }
  (void) fclose(fd);

  /* search for the key */
  offset = -1;
  for(i=0;i<720*128-6;i++)
    if(!strncmp(argv[1], &disk[i], 6)){
      if(offset >= 0)
        (void) fprintf(stderr, "duplicate serial numbers! using first\n");
      else
        offset = i - 18;
    }

  /* verify that this all makes sense */
  if(offset >= 0)
    (void) fprintf(stderr, "data file starts at offset %d\n",offset);
  else {
    (void) fprintf(stderr, "key %s not found in disk image\n",argv[1]);
    exit(1);
  }
  if(disk[offset] != 3)
    (void) fprintf(stderr, "zoffset only tested on version 3 images....\n");
  len = (disk[offset+26] << 8) | disk[offset+27];
  sum = (disk[offset+28] << 8) | disk[offset+29];
  len *= 2;
  (void) fprintf(stderr, "The image has length %d\n",len);
  if((offset + len) >= (128 * 720)){
    (void) fprintf(stderr, "The data file is a little short!\n");
    exit(1);
  }

  /* checksum the game file. checksum starts 64 bytes into disk */
  gsum = 0;
  for(i=64;i<len;i++)
    gsum += disk[offset+i];
  printf("game checksum %d  sum %d\n",sum,gsum);
  if(sum != gsum)
    printf("checksum failed\n");

  /* write it out */
  if((fd = fopen(argv[3], "w")) == NULL){
    (void) fprintf(stderr, "Can't create output file %s\n",argv[3]);
    exit(1);
  }
  if(fwrite(&disk[offset], len, 1, fd) != 1){
    (void) fprintf(stderr, "write of %s failed\n",argv[3]);
    exit(1);
  }
  (void) fclose(fd);
}


-------- zheader2.c

# include <stdio.h>
/*
 * take a 2 disk version of an infocom game an piece it together
 *
 * This should work with all of those nasty double sided atari disks
 *
 * Michael Jenkin, 1993.
 */

# define DISKSIZE (720*128)
unsigned char side1[DISKSIZE], side2[DISKSIZE];


main(argc, argv)
  int argc;
  char *argv[];
{
  unsigned char *start1, *keysearch(), *last1, *last2;
  int sum, len, obj, vcb, gbl, cmn, res, i;
  unsigned short gsum;
  FILE *fd;

  if(argc != 5){
    (void) fprintf(stderr,"usage: zheader2 <id> <side1> <side2> <outfile>\n");
    exit(1);
  }

  if(strlen(argv[1]) != 6){
    (void) fprintf(stderr, "serial id's are 6 bytes long e.g. 830329\n");
    exit(1);
  }

  /* read the files in. Nice to have enouch memory to store the disk */
  getfile(argv[2], side1);
  getfile(argv[3], side2);

  /* find the start of the game file on side1 */
  start1 = keysearch(side1, argv[1]);

  /* verify the header and extract offsets to important segments */
  printf("Version is %d\n",*start1);
  if(*start1 != 3)
    printf("Only tested on version 3's!\n");
  len = (start1[26] << 8) | start1[27];
  sum = (start1[28] << 8) | start1[29];
  cmn = (start1[24] << 8) | start1[25];
  vcb = (start1[8] << 8) | start1[9];
  obj = (start1[10] << 8) | start1[11];
  gbl = (start1[12] << 8)| start1[13];
  res = (start1[4] << 8)|start1[5];
  len *= 2;
  printf("Game file has length %d checksum %d\n",len,sum);

  printf("cmn %d vcb %d obj %d gbl %d last %d\n",
         cmn, vcb,obj,gbl,&side1[DISKSIZE]-start1);

  printf("Trying to split at res+1...\n");
  checksplit(argv[4],res+1,len, sum, start1, side2);

  printf("oh well. got to search for it. this could take a while...\n");

  for(i=vcb;i<&side1[DISKSIZE]-start1;i++)
    checksplit(argv[4], i, len, sum, start1, side2);
}

checksplit(fname, sp, len, sum, p1, p2)
     int sp, len, sum;
     unsigned char *p1, *p2;
     char *fname;
{
  unsigned short gsum;
  int i;
  FILE *fd;

  if(!(sp % 1000))
    printf("trying %d\n",sp);
  gsum = 0;
  for(i=64;i<sp;i++)
    gsum += p1[i];
  for(i=0;i<len-sp;i++)
    gsum += p2[i];
  if(gsum != sum)
    return;

  printf("split point at %d\n",sp);
  printf("checksum %d gsum %d\n",sum,gsum);

  if((fd = fopen(fname, "w")) == NULL){
    (void) fprintf(stderr,"unable to create %s\n",fname);
    exit(1);
  }
  if(fwrite(p1, sp, 1, fd) != 1){
    (void) fprintf(stderr, "write failed\n");
    exit(1);
  }
  if(fwrite(p2, len-sp, 1, fd) != 1){
    (void) fprintf(stderr, "write 2 failed\n");
    exit(1);
  }
  (void) fclose(fd);
  exit(0);
}

getfile(fname, side)
     char *fname;
     unsigned char side[DISKSIZE];
{

  FILE *fd;

  if((fd = fopen(fname, "r")) == NULL){
    (void) fprintf(stderr, "open of disk file %s failed!\n",fname);
    exit(1);
  }
  if(fread(side, DISKSIZE, 1, fd) != 1){
    (void) fprintf(stderr, "read of disk file %s failed!\n",fname);
    exit(1);
  }
  (void) fclose(fd);
}

unsigned char *keysearch(side, key)
     unsigned char side[DISKSIZE];
     char *key;
{
  unsigned char *start;
  int i;

  /* search for the key */
  for(start=side, i=0;i<DISKSIZE-6;i++,start++)
    if(!strncmp(key, start, 6)){
      return(start-18); /* offset of serial number from header */
    }
  (void) fprintf(stderr,"unable to find key %s on side1!\n",key);
}


-----------------------------------------
Return to message index