/*	mcread: macwrite.c
	Copyright (C) 1991, Mike Gleason Jr & NCEMRSoft.
	All Rights Reserved. */

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

typedef struct {
	short	type;			/* 0-1 */
	short	unused1[3];		/* 2-7 */
	long	offset;			/* 8-11 */
	short	len;			/* 12-13 */
	short	unused2;		/* 14-15 */
} MacWrite_Paragraph_Info;

#define MACWRITE_45				6
#define MACWRITE_22				3

#define DEFAULT_COMMON			" etnroaisdlhcfp"
	/* 	Most commonly occurring characters, MacWrite uses a crude
		compression scheme with these.  The default uses the American
		version. */

#define COMPRESSED_MASK			0x08

char							CommonChars[20];
int								GNNeedToReadByte = 1;	/* for Getn() */

int thrash_macwrite(in, name, dataforklen)
	FILE *in;
	char *name;
	long dataforklen;
{
	MacWrite_Paragraph_Info		P;
	long						dataOffset, infoOffset, saved;
	register long				Seek, StartOffset;
	char						paragraphStatus;
	register int				a, b, c, nibble, parIsCompressed;
	short						version, numparas, curpara;
	short						bytesInParagraph, bytesOut;
#define CheckReadErr 			if (ferror(in)) goto ReadErr
#define CheckSeekErr 			if (Seek) goto SeekErr;

	bytesOut = curpara = 0;
	(void) strcpy(CommonChars, (char *)DEFAULT_COMMON);
	
	if (dataforklen >= 0)
		(void) FindCommon(in, dataforklen);
	
	StartOffset = ftell(in);	/*	for macbinary files, this will be 128,
									otherwise it should be zero. */
									
	version = (short) Getw (in);
	CheckReadErr;
	
	if (version != MACWRITE_45)	{
		if (version == MACWRITE_22)
			(void) fprintf (stderr, "%s: macwrite format too old (2.2)\n", name);
		else if (dataforklen) {
			if (version > MACWRITE_45)
				(void) fprintf (stderr, "%s: macwrite format too new (%d)\n", name, version);
			else
				(void) fprintf (stderr, "%s: macwrite format too old (%d)\n", name, version);
		}
		return (3);
	}
	
	numparas = (short) Getw (in);
	CheckReadErr;
	
	Seek = fseek (in, (long) (StartOffset + 264), SEEK_SET);
	CheckSeekErr;
		/* should now point to the variables for the body text. */
		
	infoOffset = (long) Getl (in);
	CheckReadErr;
	
	Seek = fseek (in, (StartOffset + infoOffset), SEEK_SET);
	CheckSeekErr;
	
	/* loop through and read all the paragraphs */
	
	while (1) {
		P.type = (short) Getw (in);         if (ferror(in)) break;
		P.unused1[0] = (short) Getw (in);   if (ferror(in)) break;
		P.unused1[1] = (short) Getw (in);   if (ferror(in)) break;
		P.unused1[2] = (short) Getw (in);   if (ferror(in)) break;
		P.offset = (long) Getl (in);        if (ferror(in)) break;
		P.len = (short) Getw (in);          if (ferror(in)) break;
		P.unused2 = (short) Getw (in);      if (ferror(in)) break;
		
		dataOffset = P.offset & 0x00ffffff;		/* lo 3 bytes only */
		paragraphStatus = (char) (P.offset >> 24);		
		
		if (P.type < 0) {						/* a PICT paragraph */
			(void) puts ("### picture omitted!");
			if (CheckPage())
				return (0);
		}
		if (P.type > 0)	{						/* a text paragraph */
			saved = ftell (in);
			Seek = fseek (in, (StartOffset + dataOffset), SEEK_SET);
			CheckSeekErr;
			
			bytesInParagraph = (short) Getw (in);
			CheckReadErr;	
			
			wwInit();
			GNNeedToReadByte = 1;
			parIsCompressed = paragraphStatus & COMPRESSED_MASK;
			for (bytesOut=0; bytesOut<bytesInParagraph; bytesOut++)	{
				if (parIsCompressed) {			/*	a compressed paragraph */
					nibble = Getn (in);
					if (nibble == 0x0f) {		/*	means that next 2
													nibbles form the
													next char. */
						a = Getn (in);
						b = Getn (in);
						c = (a << 4) + b;
					}
					else c = CommonChars[nibble];
				} else							/*	a regular paragraph */
					c = getc (in);
				
				if (c == EOF) {
					(void) fprintf(stderr, "%s: unexpected end of file.\n", name);
					return (4);
				}
				
				if (wwPutchar (c & 0x00ff))
					return (0);					/* user skipped file */
			}									/* end reading the para */
			if (wwFlush())
				return (0);						/* user skipped file */
				
			Seek = fseek (in, saved, SEEK_SET);
			CheckSeekErr;
		}										/* end text paragraph */
		else {
												/*	it's a ruler, but
													who cares? */
		}
		
		if (++curpara >= numparas) break;
	}											/* paragraph loop */

	return (0);									/* exit success, mon */

SeekErr:
	(void) fprintf(stderr, "%s: seek error occurred.\n", name);
	return (5);

ReadErr:
	(void) fprintf(stderr, "%s: error occurred while trying to read the file.\n", name);
	return (6);
}	/* thrash_macwrite */




int Getn (f)
	FILE *f;
{
	static int c;
	extern int GNNeedToReadByte;
	
	if (GNNeedToReadByte) {
		c = getc(f);
		if (c == EOF) return (EOF);
		c &= 0x00ff;
		GNNeedToReadByte = 0;	/* still got the other nibble to use. */
		return (c >> 4);
	}

	GNNeedToReadByte = 1;
	return (c & 0x000f);	
}	/* Getn */



/*	This huge mess only accomplishes one thing:  read in STR ID 700 from
	the resource fork of this macwrite file.   What a waste... */
	
int FindCommon(in, dataForkLen)
	FILE *in;
	long dataForkLen;
{
#define COMMON_ID 700
	register long		rezforkstart, typeliststart, Seek;
	short				numtypes;
	register short		foundSTR;
	
	struct Oy {
		long			dataOffset, mapOffset, datalen, maplen;
	} rheader;
	
	struct Foo {
		long			crap[6];
		short			typelistOffset, namelistOffset;
	} rmapheader;
	
	struct Bar {
		char			type[5];/* really only [4], but I'm not reading in
									the whole structure at a time. */
		short			n;		/* number of rsrcs - 1*/
		short			offset;	/* from start of typelist to ref list */
	} typeinfo;
	
	struct Grok {
		short			id, nameoffset;
		long			dataoffset;  /* from start of res data; lo 3 bytes */
		long			handle;
	} refinfo;

	rezforkstart = (((128L + dataForkLen) / 128) + 1) * 128;
		/*	the resource fork starts after the header (128 bytes) and
			after the data fork, which is padded to a multiple of 128. */
			
	Seek = fseek (in, rezforkstart, SEEK_SET);
	if (Seek) goto done;		/* should be at rsrc fork start now */

	
	/* read the resource file header. */
	rheader.dataOffset = (long) Getl (in);
	if (ferror (in)) goto done;
	
	rheader.mapOffset = (long) Getl (in);
	if (ferror (in)) goto done;
	
	rheader.datalen = (long) Getl (in);
	if (ferror (in)) goto done;
	
	rheader.maplen = (long) Getl (in);
	if (ferror (in)) goto done;
	
	
	/* read the resource MAP header. */
	Seek = fseek (in, (long) (rezforkstart + rheader.mapOffset + 24L), SEEK_SET);
	if (Seek) goto done;
	
	rmapheader.typelistOffset = (short) Getw (in); /* 6 craps * 4 bytes = 24L bytes. */
	if (ferror (in)) goto done;
	
	
	/* read the typelist. */
	Seek = fseek (in, (long) (rezforkstart + rheader.mapOffset +
		rmapheader.typelistOffset), SEEK_SET);
	if (Seek) goto done;
	
	typeliststart = ftell (in);
	
	numtypes = (short) Getw (in) + 1;	/* rez manager stores (numtypes - 1) ! */
	if (ferror(in)) goto done;
	
	foundSTR = 0;
	while (!foundSTR && --numtypes >= 0) {
		/* read in some info about the next type in the list. */
		typeinfo.type[0] = (char) getc (in);
		typeinfo.type[1] = (char) getc (in);
		typeinfo.type[2] = (char) getc (in);
		typeinfo.type[3] = (char) getc (in);
		typeinfo.type[4] = '\0';
		if (ferror (in)) goto done;
		typeinfo.n = (short) Getw (in) + 1;	/* rez manager stores (numrsrcs - 1) ! */
		if (ferror (in)) goto done;
		typeinfo.offset = (short) Getw (in);
		if (ferror (in)) goto done;
		
		foundSTR = (strcmp (STR_TYPE, typeinfo.type) == 0);
	}
	if (!foundSTR) goto done;	/* no 'STR '! */
	Seek = fseek (in, (long) (typeliststart + typeinfo.offset), SEEK_SET);
	if (Seek) goto done;
		
	while (--typeinfo.n >= 0) {
		refinfo.id = (short) Getw (in);
		if (ferror (in)) goto done;
		refinfo.nameoffset = (short) Getw (in);
		if (ferror (in)) goto done;
		refinfo.dataoffset = (long) Getl (in);
		if (ferror (in)) goto done;
		refinfo.handle = (long) Getl (in);
		if (ferror (in)) goto done;

		if (refinfo.id == COMMON_ID) break;
	}
	if (refinfo.id != COMMON_ID) goto done;		/* no CommonChars! */
	
	refinfo.dataoffset &= 0x00ffffff;	/* lo 3 bytes for offset */
	Seek = fseek (
		in, 
		(long) (rezforkstart + refinfo.dataoffset +
			rheader.dataOffset + 5L),	/*	4L for the length of the rsrc,
											which we already know, and
											another 1L for the pascal
											string's length byte. */
		SEEK_SET);
	if (Seek) goto done;
	
	(void) fgets (CommonChars, 18, in);
	CommonChars[16] = '\0';

done:
	Seek = fseek (in, (long) 128, SEEK_SET);
}

/* eof */
