/***********************************************************************\
PICT file class for Convert PICT which converts graphics from PICT to eps formats.
Copyright (C) 1993 David John Burrowes

This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 1, or (at your option) any later version.

This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.

The author, David John Burrowes, can be reached at:
	davidjohn@kira.net.netcom.com
	David John Burrowes
	1926 Ivy #10
	San Mateo, CA 94403-1367
\***********************************************************************/


/*
====================================================================
This is the implementation file for the PICTFile class.  Full documentation for this class can be found in the PICTFile.rtf file.  I will not duplicate all that fine information here.
	This is $Revision: 1.10 $ of this file
	It was last modified by $Author: death $ on $Date: 93/04/04 23:29:46 $
Note that this file was created while using the New Century Schoolbook Roman typeface.  You may find that some things line up strangely if you don't use that family.
* $Log:	PICTFile.m,v $
Revision 1.10  93/04/04  23:29:46  death
Sun Apr  4 23:29:45 PDT 1993

Revision 1.9  93/01/09  21:07:04  death
Sat Jan  9 21:07:04 PST 1993

Revision 1.8  93/01/01  11:51:13  death
Fri Jan  1 11:51:12 PST 1993

Revision 1.7  92/12/31  15:34:01  death
Thu Dec 31 15:34:01 PST 1992

Revision 1.6  92/12/05  23:06:55  death
Sat Dec  5 23:06:54 PST 1992

Revision 1.5  92/12/03  18:01:12  death
Thu Dec  3 18:01:11 PST 1992

Revision 1.4  92/11/27  19:37:24  death
Fri Nov 27 19:37:24 PST 1992

Revision 1.3  92/11/08  09:26:15  death
Sun Nov  8 09:26:15 PST 1992

Revision 1.2  92/08/09  19:46:19  death
Sun Aug  9 19:46:19 PDT 1992

Revision 1.1  92/08/09  08:53:00  death
Sun Aug  9 08:52:59 PDT 1992

*====================================================================
*/

//
//	Import our own definition
//
#import "PICTFile.h"
#import <architecture/byte_order.h>	// for big/little endian conversins


@implementation PICTFile


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		free
//	Parameters:	none
//	Returns:		self
//	Stores:		none
//	Description:
//		Frees various data owned by the instance.  
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- free
{
	if (PICTHeader != NullByteString)
		FreeByteString(PICTHeader);
	if (BoundingRect != (PICTRect*) NullByteString)
		FreeByteString((ByteString)BoundingRect);
	return [super   free];
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		OpenWithAccess:
//	Parameters:	the means of accessing the specified file
//	Returns:		self
//	Stores:		none
//	Description:
//		This is a simple subclass-ed method from the File class.  This simply traps to
//		make sure that the operation is not one that involves file writing, as this is not
//		supported at present by the PICTFile class.  If it is an operation involving
//		writing, and error is set, and this returns self.
//	Bugs:
//		We don't check for errors when reading the header or opcodes or anything...
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- OpenWithAccess: (AccessType) operation
{
	PICTOpcode	theOpcode;
	[self ResetResults];
	
	if (operation != FILE_READ)
	{
		[self   StoreErrorCode: ERR_MAYNOTWRITE 
			AndText: "You may ONLY read from a PICT file."];
	}
	else
	{
		[super   OpenWithAccess: operation];
		if ([self   GetErrorCode] == ERR_OK)
		{
			PICTHeader = NewByteString(512);
			[self   Read: 512 BytesInto: PICTHeader];
			[self   AdvanceBytes: 2];	// skip the size
			BoundingRect = [self   GetRect];
			//
			//	92.12.03	djb	Added because I found a Superpaint doc which had a large
			//				number of 0's after the bounding rect.  Weird.
			//
			do
			{
				theOpcode = [self   GetOpcode];
			}
			while( (theOpcode == 0) && ([self   GetErrorCode] != ERR_EOF));
			
			if (theOpcode == 0x11)
			{
				switch ([self   GetByte])
				{
					case 0x01:
						PICTVersion = 1;
						break;
					case 0x02:
						PICTVersion = 2;
						break;
				}
			}
			if (PICTVersion == 0)
			{
				PICTVersion = 9999;	// unlikely Apple will ever do a 9999 vers. =)
				[self   StoreErrorCode: ERR_UNKNOWNVERSION
					AndText: "Unknown PICT version!"];
			}
		}
	}

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		initAndUse:
//	Parameters:	none
//	Returns:		self
//	Stores:		none
//	Description:
//		This is a simple subclass-ed method from the File class.  We subclass it
//		because we need to initalize our instance variables.
//	Bugs:
//		We don't initalize the bounding rect
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- initAndUse: (roCString) pathname
{
	[super initAndUse: pathname];
	
	PICTHeader = NullByteString;
	PICTVersion = 0;
	FoundEnd = NO;

	return self;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		initAndUseTemporary
//	Parameters:	none
//	Returns:		self
//	Stores:		none
//	Description:
//		This is a simple subclass-ed method from the File class.  This method essentially
//		implies doing write access to a file, which we don't presently support, so we just
//		return an error indirectly by setting FoundEnd to be YES.
//	Bugs:
//		We should be setting an error here.
//		We don't initalize the bounding rect
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- initAndUseTemporary
{
	[super initAndUseTemporary];
	
	PICTHeader = NullByteString;
	PICTVersion = 0;
	FoundEnd = YES;
	return self;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetVersion
//	Parameters:	none
//	Returns:		an integer representing the PICT file version number
//	Stores:		none
//	Description:
//		Returns the version number of the PICT file to the caller.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Integer) GetVersion
{
	return PICTVersion;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetHeader
//	Parameters:	none
//	Returns:		A pointer to a block of constant data that is a copy of the 512byte
//				pict file header. 
//	Stores:		none
//	Description:
//		Returns a pointer to the 512 byte header of the PICT file.  The 512 bytes are
//		not terminated in any way, and it is assumed the caller is aware of this.
//	Bugs:
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (ConstByteString) GetHeader
{
	return PICTHeader;
}

//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetBounds
//	Parameters:	none
//	Returns:		A pointer to a new struct describing the bounds of the pict file
//	Stores:		none
//	Description:
//		Returns a rectangle struct to the caller containing the boundrary rectangl
//		of the PICT image in this PICT file.  The caller is responsible for disposing of
//		the rectangle.
//	Bugs:
//		We blindly assume the memory allocation worked.
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PICTRect*) GetBounds
{
	PICTRect*	tempRect;

	tempRect = (PICTRect*) NewByteString(sizeof(PICTRect));
	tempRect->left = BoundingRect->left;
	tempRect->right = BoundingRect->right;
	tempRect->top = BoundingRect->top;
	tempRect->bottom = BoundingRect->bottom;

	return tempRect;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetOpcode
//	Parameters:	none
//	Returns:		A PICT opcode
//	Stores:		none
//	Description:
//		Reads the next opcode from the PICT file and returns it.  This reads either a
//		one or two byte opcode, depending on whether it's version 1 or 2 (or above).  If
//		the version is 2 or above, it will also automatically move to the next even word
//		boundrary to read the next opcode.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//	Bugs:
//		By always returning the FF opcode when we've reached the end, things must
//		be carefull that they don't loop forever somehow....
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PICTOpcode) GetOpcode
{
	PICTOpcode	theOpcode;
	
	if (FoundEnd == YES)
		theOpcode = 0xFF;
	else
	{
		switch (PICTVersion)
		{
			case 0:
			case 1: 
				theOpcode = [self   ReadByte];
				break;
			case 2:
				//
				//	Pict 2 opcodes always occur on 16 bit boundaries.  If we aren't on
				//	an even byte number, then scoot forward a byte.
				//
				if (EvenUnsignedNum([self   GetCurrentPosition]) != YES)
					[self   AdvanceBytes: 1];
				[self   Read: 2 BytesInto: (ByteString) &theOpcode];
				theOpcode = (PICTOpcode)NXSwapBigShortToHost(theOpcode);
				break;
			default:
				//
				// if we don't know the current version, try to quit with the quit opcode.
				//
				theOpcode = 0xFF;
				FoundEnd = YES;
				break;
		}
		if (theOpcode == 0xFF)
			FoundEnd = YES;
	}
	return theOpcode;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetRect
//	Parameters:	none
//	Returns:		A Pict rectangle  (ptr to)  (or pass NULL if an erorr occurred)
//	Stores:		none
//	Description:
//		Reads an 8 byte PICT rectangle from the PICT file, and returns this
//		copy to the caller.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
//		We set no error codes.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PICTRect*) GetRect
{
	PICTRect*	tempRect;

	tempRect = (PICTRect*) NewByteString(sizeof(PICTRect));

	[self   Read: 8 BytesInto: (ByteString) tempRect];
	tempRect->left = (INTEGER) NXSwapBigShortToHost(tempRect->left);
	tempRect->right = (INTEGER) NXSwapBigShortToHost(tempRect->right);
	tempRect->top = (INTEGER) NXSwapBigShortToHost(tempRect->top);
	tempRect->bottom = (INTEGER) NXSwapBigShortToHost(tempRect->bottom);


	if ([self   GetErrorCode] != ERR_OK)
	{
		FreeByteString((ByteString) tempRect);
		tempRect = NULL;
	}

	return tempRect;
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetPoint
//	Parameters:	none
//	Returns:		A Pict point  (ptr to)  (or pass NULL if an erorr occurred)
//	Stores:		none
//	Description:
//		Reads a 4 byte point from the PICT file and returns a copy to the caller.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
//		We set no error codes.
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PICTPoint*) GetPoint
{
	PICTPoint*	tempPt;

	tempPt = (PICTPoint*) NewByteString(sizeof(PICTPoint));

	[self   Read: 4 BytesInto: (ByteString)tempPt];
	tempPt->x = (INTEGER) NXSwapBigShortToHost(tempPt->x);
	tempPt->y = (INTEGER) NXSwapBigShortToHost(tempPt->y);

	if ([self   GetErrorCode] != ERR_OK)
	{
		FreeByteString((ByteString) tempPt);
		tempPt = NULL;
	}

	return tempPt;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetByte
//	Parameters:	none
//	Returns:		A byte
//	Stores:		none
//	Description:
//		This is just like ReadByte in the File class.  It reads a byte and returns it
//		to the caller.  This is provided for naming consistancy with the other methods here.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Byte) GetByte
{
	return	[self   ReadByte];
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetSignedByte
//	Parameters:	none
//	Returns:		A byte
//	Stores:		none
//	Description:
//		This is just like ReadByte in the File class.  It reads a byte and returns it
//		to the caller.  This is provided for naming consistancy with the other methods here.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (SignedByte) GetSignedByte
{
	return	(SignedByte) [self   ReadByte];
}



//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetPString
//	Parameters:	none
//	Returns:		A CString, or a NullCString if a problem occurred.
//	Stores:		none
//	Description:
//		This reads in a Pascal style string from the PICT file, and returns the copy
//		as a CString structure to the caller (who is responsible for disposing of it)
//		Note, though, thatthe file reading routines it calls may store errors...
//	Bugs:
//		Not checking them error codes again....
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (CString) GetPString
{
	Byte	length;
	CString	theString;
	
	length = [self   ReadByte];
	theString = NewCString(length);
	[self   Read: length BytesInto: (ByteString) theString];
	theString[length] = EndOfCString;

	return theString;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetINTEGER
//	Parameters:	none
//	Returns:		An integer
//	Stores:		none
//	Description:
//		This reads a 2 byte signed integer in and then returns it to the caller as an
//		Integer type
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Integer) GetINTEGER
{
	Signed16Bits	theInt;	
	[self   Read: 2 BytesInto:  (ByteString) &theInt];
	return 	(INTEGER) NXSwapBigShortToHost(theInt);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetPositiveINTEGER
//	Parameters:	none
//	Returns:		A positive integer
//	Stores:		none
//	Description:
//		This reas a 2 byte signed integer in and then returns it to the caller as a
//		PositiveInteger type.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PositiveInteger) GetPositiveINTEGER
{
	bits16	theInt;	
	[self   Read: 2 BytesInto:  (ByteString) &theInt];
	return 	(bits16) NXSwapBigShortToHost(theInt);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetLONGINT
//	Parameters:	none
//	Returns:		A long int
//	Stores:		none
//	Description:
//		This reads a 4 byte signed integer in and then returns it to the caller as an
//		Integer type.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Integer) GetLONGINT
{
	Signed32Bits	theInt;	
	[self   Read: 4 BytesInto:  (ByteString) &theInt];
	return 	(LONGINT) NXSwapBigLongToHost(theInt);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetPositiveLONGINT
//	Parameters:	none
//	Returns:		A positive integer
//	Stores:		none
//	Description:
//		This reas a 2 byte signed integer in and then returns it to the caller as a
//		PositiveInteger type.
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (PositiveInteger) GetPositiveLONGINT
{
	bits32	theInt;	
	[self   Read: 4 BytesInto:  (ByteString) &theInt];
	return 	(bits32) NXSwapBigLongToHost(theInt);
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetFixedNumber
//	Parameters:	none
//	Returns:		A real number
//	Stores:		none
//	Description:
//		This reas a 4 byte signed integer in and then converts it to the fixed pont number
//		it really is by shoving a decimal point in after the 16th bit, and returning as
//		a real number..
//	History:
//		93.08.01	djb	Added NXSwap code for endianness
//		93.08.15	djb	Argh!  I need to be casting the results!!!
//		93.08.16	djb	Argh! again! Wasn't casting properly in all cases.
//	Bugs:
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (Real) GetFixedNumber
{
	Signed32Bits	fixedNumber;
	[self   Read: 4 BytesInto:  (ByteString) &fixedNumber];
	fixedNumber = (LONGINT) NXSwapBigLongToHost(fixedNumber);
	return fixedNumber / 65536.0;
}


//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//	Routine:		GetPolyVerbs
//	Parameters:	none
//	Returns:		an 8 bit bitfield of options for drawing a polygon.
//	Stores:		none
//	Description:
//		This merly provides a way for the app to read in the bitfield that is used for
//		one of the piccomments.  This way if we need to ever tweak the data, we can do
//		it just here.
//	Bugs:
//		This does some really gross typecasting to convert the 1 byte value to a 1 byte
//		bitfield.  I'm sure there are easier ways...
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
- (TPolyVerbRec) GetPolyVerbs
{
	TPolyVerbRec * dummy;
	Byte  temp = [self   ReadByte];
	dummy =  (TPolyVerbRec *) & temp;
	return *dummy;
}


@end

