/*
** dcmtoatr.c -- written by Chad Wagner
**
** Revision History:
** 31 May 95  cmwagner@gate.net
**  1 Jun 95  cmwagner@gate.net
**            added in some portability macros from dcm.c
**            added in read_atari16() function from dcm.c
**            wrote write_atari16() function
**            did a general clean-up of the code
**  1 Jun 95  crow@cs.dartmouth.edu
**            did a clean-up of the code, hopefully resolving any
**            portability issues
**  2 Jun 95  cmwagner@gate.net and crow@cs.dartmouth.edu
**	      Allow for multi-file DCM files, after they've been
**	      combined into a single file
*/

/* Include files */
#include <stdio.h>
#include <string.h>

/* prototypes */
void decode_C1(void);
void decode_C3(void);
void decode_C4(void);
void decode_C6(void);
void decode_C7(void);
void decode_FA_atr(void);
int  read_atari16(FILE *);
void write_atari16(FILE *,int);

/*
** Portability macros
** 1  Jun 95  crow@cs.dartmouth.edu (Preston Crow)
*/
#if defined(__MSDOS) || defined(__MSDOS__) || defined(_MSDOS) || defined(_MSDOS_)
#define MSDOS /* icky, icky, icky! */
#endif

/* version of this program, as seen in usage message */
#define VERSION "1.1"

/* Global variables */
FILE		*fin,*fout;
unsigned int	secsize;
unsigned short	cursec=0,maxsec=0;
unsigned char	createdisk=0,working=0,last=0,density,buf[256];
int		doprint=1; /* True for diagnostic output */

/*
** main()
*/
int main(int argc,char **argv)
{
	unsigned char	archivetype; /* Block type for first block */
	unsigned char	blocktype; /* Current block type */
	unsigned char	tmp; /* Temporary for read without clobber on eof */
	unsigned char	imgin[256],imgout[256]; /* file names */
	unsigned char	done=0; /* completion flag */
	char		*self; /* program name from argv[0] */

#ifdef MSDOS
	if ((self = strrchr(argv[0],'\\')) == NULL)
#else
	if ((self = strrchr(argv[0],'/')) == NULL)
#endif
	  {
		  self = argv[0];
	  }
	else
	  self++; /* Skip the slash */

	--argc;++argv; /* Don't look at the filename anymore */
	/* Process switches */
	if (argc) while (*argv[0]=='-') {
		int nomore=0;

		++argv[0]; /* skip the '-' */
		while(*argv[0]) {
			switch(*argv[0]) {
			      case '-':
				nomore=1;
				break;
			      case 'q':
			      case 'Q':
				doprint = !doprint;
				break;
			      default:
				fprintf(stderr,"Unsupported switch:  %c\n",*argv[0]);
				fprintf(stderr,"%s "VERSION" by cmwagner@gate.net\n",self);
				fprintf(stderr,"%s [-q] input[.dcm] [output[.atr]]\n",self);
				exit(1);
			}
			++argv[0]; /* We've processed this flag */
		}
		--argc;++argv; /* Done processing these flags */
		if(nomore) break; /* Filename may begin with '-' */
	}

	if (argc<1 || argc>2) {
		fprintf(stderr,"%s "VERSION" by cmwagner@gate.net\n",self);
		fprintf(stderr,"%s [-q] input[.dcm] [output[.atr]]\n",self);
		exit(1);
	}

	strcpy(imgin,argv[0]);
	if (strrchr(imgin,'.') == NULL)
		strcat(imgin,".dcm");

	if (argc==2)
		strcpy(imgout,argv[1]);
	else {
		char *p;

		strcpy(imgout,imgin);
		if ((p = strrchr(imgout,'.')) != NULL)
			*p = 0;
	}
	if (strrchr(imgout,'.') == NULL)
		strcat(imgout,".atr");

	if ((fin = fopen(imgin,"rb")) == NULL) {
		fprintf(stderr,"I couldn't open \"%s\" for reading.\n",imgin);
		exit(1);
	}

	archivetype = blocktype = fgetc(fin);
	switch(blocktype) {
		case 0xF9:
		case 0xFA:
			break;
		default:
			fprintf(stderr,"0x%02X is not a known header block.\n",blocktype);
			exit(1);
	}

	if ((fout = fopen(imgout,"rb")) != NULL) {
		fprintf(stderr,"I can't use \"%s\" for output, it already exists.\n",imgout);
		exit(1);
	} else {
		fout = fopen(imgout,"wb");
	}

	rewind(fin);

	do {
		if(doprint) printf("\rCurrent sector: %4u",cursec);

#ifdef MSDOS
		if (kbhit()) {
			if (getch() == 27) {
				fprintf(stderr,"  Processing terminated by user.\n");
				exit(1);
			}
		}
#endif
                if (feof(fin)) {
			fflush(stdout); /* Possible buffered I/O confusion fix */
                        if ((!last) && (blocktype == 0x45) && (archivetype == 0xF9)) {
				fprintf(stderr,"\nMulti-part archive error.\n");
				fprintf(stderr,"To process these files, you must first combine the files into a single file.\n");
#ifdef MSDOS
				fprintf(stderr,"\tCOPY /B file1.dcm+file2.dcm+file3.dcm newfile.dcm\n");
#else
				fprintf(stderr,"\tcat file1.dcm file2.dcm file3.dcm > newfile.dcm\n");
#endif
                        }
			else {
				fprintf(stderr,"  EOF before end block.\n");
			}
                        exit(1);
                }

		if (working) {
			if (((((long)cursec - 1) * secsize) + 16) != ftell(fout)) {
				fprintf(stderr,"  Output has desynched.\nfin=%lu fout=%lu != %lu "
				"cursec=%u secsize=%u\n",ftell(fin),ftell(fout),
				(((long)cursec - 1) * secsize) + 16,cursec,secsize);
				exit(1);
			}
		}

		tmp = fgetc(fin); /* blocktype is needed on EOF error--don't corrupt it */
		if (feof(fin)) continue; /* Will abort on the check at the top of the loop */
		blocktype = tmp;
		switch(blocktype) {
		      case 0xF9:
		      case 0xFA:
			/* New block */
			decode_FA_atr();
			break;
		      case 0x45:
			/* End block */
			working=0;
			if (last) {
				if (doprint) printf("\r%s has been successfully decompressed.\n",imgout);
				fclose(fin);
				fclose(fout);
				done=1; /* Normal exit */
			}
			break;
		      case 0x41:
		      case 0xC1:
			decode_C1();
			break;
		      case 0x43:
		      case 0xC3:
			decode_C3();
			break;
		      case 0x44:
		      case 0xC4:
			decode_C4();
			break;
		      case 0x46:
		      case 0xC6:
			decode_C6();
			break;
		      case 0x47:
		      case 0xC7:
			decode_C7();
			break;
		      default:
			fprintf(stderr,"\n\n0x%02X is not a known block type.  File may be "
				"corrupt.\n",blocktype);
			exit(1);
		} /* end case */

		if ((blocktype != 0x45) && (blocktype != 0xFA) &&
		(blocktype != 0xF9)) {
			if (!(blocktype & 0x80)) {
				cursec=read_atari16(fin);
				fseek(fout,(((long)cursec - 1) * secsize) + 16,SEEK_SET);
			} else {
				cursec++;
			}
		}
	} while(!done); /* end do */
	return(0); /* Should never be executed */
}

void decode_C1(void)
{
	unsigned char  secoff,tmpoff,c;

	tmpoff=fgetc(fin);
	c=fgetc(fin);
	for (secoff=0; secoff<secsize; secoff++) {
		buf[secoff]=c;
	}
	c=tmpoff;
	for (secoff=0; secoff<tmpoff; secoff++) {
		c--;
		buf[c]=fgetc(fin);
	}
	fwrite(buf,secsize,1,fout);
}

void decode_C3(void)
{
	unsigned char  secoff,tmpoff,c;

	secoff=0;
	do {
		tmpoff=fgetc(fin);
		for (; secoff<tmpoff; secoff++) {
			buf[secoff]=fgetc(fin);
		}
		if (secoff == secsize)
			break;
		tmpoff=fgetc(fin);
		c=fgetc(fin);
		for (; secoff<tmpoff; secoff++) {
			buf[secoff] = c;
		}
	} while(secoff < secsize);
	fwrite(buf,secsize,1,fout);
}

void decode_C4(void)
{
	unsigned char  secoff,c;

	c=fgetc(fin);
	for (secoff=c; secoff<secsize; secoff++) {
		buf[secoff]=fgetc(fin);
	}
	fwrite(buf,secsize,1,fout);
}

void decode_C6(void)
{
	fwrite(buf,secsize,1,fout);
}

void decode_C7(void)
{
	fread(buf,secsize,1,fin);
	fwrite(buf,secsize,1,fout);
}

void decode_FA_atr(void)
{
	unsigned char c;

	if (working) {
		fprintf(stderr,"\nTrying to start section but last section never had "
		"an end section block.\n");
		exit(1);
	}
	c=fgetc(fin);
	density=((c & 0x70) >> 4);
	last=((c & 0x80) >> 7);
	switch(density) {
	      case 0:
		maxsec=720;
		secsize=128;
		break;
	      case 2:
		maxsec=720;
		secsize=256;
		break;
	      case 4:
		maxsec=1040;
		secsize=128;
		break;
	      default:
		fprintf(stderr,"  Density type is unknown, density type=%u\n",density);
		exit(1);
	}

	if (createdisk == 0) {
		createdisk = 1;
		/* write out atr header */
		/* special code, 0x0296 */
		write_atari16(fout,0x296);
		/* image size (low) */
		write_atari16(fout,(short)(((long)maxsec * secsize) >> 4));
		/* sector size */
		write_atari16(fout,secsize);
		/* image size (high) */
		write_atari16(fout,(short)(((long)maxsec * secsize) >> 20));
		/* 8 bytes unused */
		write_atari16(fout,0);
		write_atari16(fout,0);
		write_atari16(fout,0);
		write_atari16(fout,0);
		memset(buf,0,256);
		for (cursec=0; cursec<maxsec; cursec++) {
			fwrite(buf,secsize,1,fout);
		}
	}
	cursec=read_atari16(fin);
	fseek(fout,(((long)cursec - 1) * secsize) + 16,SEEK_SET);
	working=1;
}

/*
** read_atari16()
** Read a 16-bit integer with Atari byte-ordering.
** 1  Jun 95  crow@cs.dartmouth.edu (Preston Crow)
*/
int read_atari16(FILE *fin)
{
	int ch_low,ch_high; /* fgetc() is type int, not char */

	ch_low = fgetc(fin);
	ch_high = fgetc(fin);
	return(ch_low + 256*ch_high);
}

void write_atari16(FILE *fout,int n)
{
	unsigned char ch_low,ch_high;

	ch_low = (unsigned char)(n&0xff);
	ch_high = (unsigned char)(n/256);
	fputc(ch_low,fout);
	fputc(ch_high,fout);
}
