/*

  Program to massage a captured DMP file into a .DSK (or other binary) file.

  To make the dump, use a terminal program to capture the output from the
  following RSX command sequence:

  >INS $DMP
  >DMO ddu:/DEV/LOCK=V      (only needed if ddu: is system disk)
  >MOU ddu:/FOR             (needed if ddu: is *not* the system disk)
  >DMP TI:=ddu:/BL:0/WD

  By John Wilson <wilson@dbit.com>.

  03/07/99	JMBW	Created (for reading octal dumps via E11 console).
  07/19/99	JMBW	Modified for DMP /WD (word hex dump, nice & concise).
  07/31/99	JMBW	Cleaned up for release.

*/

#include <stdio.h>

char lbuf[81];
unsigned char obuf[8192];
int optr=0;

FILE *in, *out;

/* parse octal value from next n chars */
unsigned long octal(char **p,int n)
{
	unsigned long v=0UL;
	char c;

	while(n--) {
		c=*((*p)++);		/* get next dig */
		if(c<'0'||c>'7') {	/* must be octal */
			*p=NULL;
			return(0UL);
		}
		v=(v<<3)|(c-'0');	/* add it in */
	}
	return(v);
}

/* as above, variable size field */
unsigned long voctal(char **p)
{
	unsigned long v=0UL;
	char c;
	int ok=0;

	for(;;) {
		c=**p;			/* get next dig */
		if(c<'0'||c>'7') {	/* must be octal */
			if(ok) break;
			*p=NULL;
			return(0UL);
		}
		v=(v<<3)|(c-'0');	/* add it in */
		(*p)++;
		ok=1;			/* got a digit */
	}
	return(v);
}

/* parse hex value from next n chars */
unsigned long hex(char **p,int n)
{
	unsigned long v=0UL;
	char c;

	while(n--) {
		c=*((*p)++);		/* get next dig */
		if(c>='0'&&c<='9')	/* digit */
			v=(v<<4)|(c-'0');  /* add it in */
		else if(c>='A'&&c<='F')	/* letter A-F (*not* LC) */
			v=(v<<4)|(c-'A'+0x0A);
		else {			/* something else, illegal */
			*p=NULL;
			return(0UL);
		}
	}
	return(v);
}

/* make sure next n characters match a fixed string */
int match(char **p,char *q) {
	register char c;

	while(*q) {
		c=*((*p)++);	/* get next char */
		if(c>='a'&&c<='z') c&=~040;  /* convert to UC */
		if(c!=*q++) return(0);  /* no match */
	}
	return(1);		/* made it all the way through */
}

main(int argc,char **argv)
{
	unsigned long addr, data, block, line, temp;
	static unsigned char bytes[16];
	int n;
	char *p;

	if(argc!=3) {
		fprintf(stderr,"Usage:  %s infile outfile\n",argv[0]);
		exit(1);
	}

	/* open input file */
	if((in=fopen(argv[1],"r"))==NULL) {
		perror("opening input file");
		exit(1);
	}

	/* open output file */
	if((out=fopen(argv[2],"wb"))==NULL) {
		perror("opening output file");
		exit(1);
	}

	line=0UL;		/* nothing read yet */
	block=0UL;		/* assume block 0 */
	addr=0UL;		/* start of block */

	for(;;) {
		/* get next line */
		if(fgets(lbuf,sizeof(lbuf),in)==NULL) {
			if(ferror(in)) {
				fprintf(stderr,
				"?File read error at line %ld\n",
					line);
				goto err;
			}
			else break;
		}
		line++;

		/* try it as a block heading */
		p=lbuf;
		if(match(&p,"\t\t LOGICAL BLOCK ")) {
			/* looks good, parse block number */
			temp=voctal(&p);
			if(p==NULL) goto next;
			if(!match(&p,",")) goto next;
			temp=(temp<<16)|octal(&p,6);
			if(p==NULL) goto next;

			/* should be the next block in sequence */
			if(temp!=block) {
				fprintf(stderr,
				"?Block sequence error at line %ld\n",
					line);
				goto err;
			}

			/* should have finished all 512 bytes of prev blk */
			if(addr!=0) {
				fprintf(stderr,
				"?Incomplete preceding block at line %ldn",
					line);
				goto err;
			}
		}
		else {
			/* nope, try it as 8 hex words and a hex address */
			p=lbuf;
			for(n=16;n;n-=2) {
				temp=hex(&p,4);		/* get a # */
				if(p==NULL) goto next;
				bytes[n-2]=temp&0xFF;	/* save 2 bytes */
				bytes[n-1]=temp>>8;
				if(!match(&p," "))	/* blank follows */
					goto next;	/* (even on 8th) */
			}
			if(!match(&p,"\t"))		/* tab */
				goto next;
			temp=hex(&p,4);			/* addr follows */
			if(p==NULL) goto next;

			/* make sure it's the next line in sequence */
			if(temp!=addr) {
				fprintf(stderr,
				"?Data sequence error at line %ld\n",
					line);
				goto err;
			}

			/* write the 16 bytes to output file buffer */
			for(n=0;n<16;n++) {
				obuf[optr++]=bytes[n];
				if(optr==sizeof(obuf)) {
					if(fwrite(obuf,1,optr,out)!=optr) {
						perror("?Error writing file");
						goto err;
					}
					optr=0;
				}
			}

			/* prepare for next data line */
			addr+=0x10;		/* bump to next line */
			if(addr==512) {		/* wrap past end of block */
				addr=0;
				block++;
			}
		}
	next:	;
	}

	/* should have ended on a block boundary */
	if(addr!=0) {
		fprintf(stderr,
		"?Incomplete final block (next seq = %04lX, blk=%ld)\n",
			addr,block);
		exit(1);
	}

	/* tell them what happened */
	fprintf(stderr,"Total blocks written:  %ld\n",block);

	/* flush buffer */
	if(optr) {
		if(fwrite(obuf,1,optr,out)!=optr) {
			perror("fwrite");
			exit(1);
		}
	}
	fclose(in);
	fclose(out);
	exit(0);

err:	fclose(in);
	fclose(out);
	exit(1);
}

