/*
** Copyright (c) 1989, 1990
** Truevision, Inc.
** All Rights Reserverd
**
** TGAPACK reads the contents of a Truevision(R) TGA(tm) File and provides
** the ability to compress the image data via run length encoding, or
** to uncompress images that have been stored in a run length encoded
** format.  The program only operates on files stored in the original
** TGA format.
**
** USAGE:
**		tgapack [options] [file1] [file2] ...
**
** If no filenames are provided, the program will prompt for a file
** name.  If no extension is provided with a filename, the program
** will search for the file with ".tga", ".vst", ".icb", ".vda", or
** ".win" extension.  Options are specified by a leading '-'.
** If no options are provided, the program attempts to compress an
** uncompressed image.
**
** Recognized options are:
**
**		-unpack			uncompressed a run length encoded image
**		-32to24			compress a 32 bit image by eliminating alpha data
**		-version		report version number of program
*/

#include <conio.h>
#include <graph.h>
#include <io.h>
#include <malloc.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "tga.h"

/*
** Define byte counts associated with extension areas for various
** versions of the TGA specification.
*/
#define	EXT_SIZE_20	495			/* verison 2.0 extension size */

#define CBUFSIZE	2048		/* size of copy buffer */
#define RLEBUFSIZ	512			/* size of largest possible RLE packet */


extern void		main( int, char ** );
extern int		CountDiffPixels( char *, int, int );
extern long		CountRLEData( FILE *, unsigned int, unsigned int, int );
extern int		CountSamePixels( char *, int, int );
extern int		DisplayImageData( unsigned char *, int, int );
extern UINT32	GetPixel( unsigned char *, int );
extern int		OutputTGAFile( FILE *, FILE *, TGAFile * );
extern int		ParseArgs( int, char ** );
extern void		PrintImageType( int );
extern void		PrintTGAInfo( TGAFile * );
extern UINT8	ReadByte( FILE * );
extern void		ReadCharField( FILE *, char *, int );
extern UINT32	ReadLong( FILE * );
extern int		ReadRLERow( unsigned char *, int, int, FILE * );
extern UINT16	ReadShort( FILE * );
extern int		RLEncodeRow( char *, char *, int, int );
extern char		*SkipBlank( char * );
extern void		StripAlpha( unsigned char *, int );
extern int		WriteByte( UINT8, FILE * );
extern int		WriteLong( UINT32, FILE * );
extern int		WriteShort( UINT16, FILE * );
extern int		WriteStr( char *, int, FILE * );


/*
** String data for interpretting image orientation specification
*/
char	*orientStr[] =
{
	"Bottom Left",
	"Bottom Right",
	"Top Left",
	"Top Right"
};

/*
** String data for interpretting interleave flag defined with VDA
** This field is now obsolete and should typically be set to zero.
*/
char	*interleaveStr[] =
{
	"  Two Way (Even-Odd) Interleave (e.g., IBM Graphics Card Adapter), Obsolete",
	"  Four Way Interleave (e.g., AT&T 6300 High Resolution), Obsolete",
};


/*
** Filename extensions used during file search
*/
char	*extNames[] =
{
	".tga",
	".vst",
	".icb",
	".vda",
	".win",
	NULL
};

TGAFile			f;				/* control structure of image data */

int				unPack;			/* when true, uncompress image data */
int				noAlpha;		/* when true, converts 32 bit image to 24 */

int				inRawPacket;	/* flags processing state for RLE data */
int				inRLEPacket;	/* flags processing state for RLE data */
unsigned int	packetSize;	/* records current RLE packet size in bytes */
char			rleBuf[RLEBUFSIZ];

char		copyBuf[CBUFSIZE];


char		*versionStr =
"Truevision(R) TGA(tm) File Compression Utility Version 1.3 - January 2, 1990";


void
main( argc, argv )
int argc;
char **argv;
{
	int			fileFound;
	int			fileCount;
	int			files;
	char		*q;
	FILE		*fp, *outFile;
	int			i;
	char		fileName[80];
	char		outFileName[80];
	struct stat	statbuf;

	unPack = 0;			/* default to compressing image data */
	noAlpha = 0;		/* default to retaining all components of 32 bit */

	inRawPacket = inRLEPacket = 0;	/* initialize RLE processing flags */
	packetSize = 0;
	/*
	** The program can be invoked without an argument, in which case
	** the user will be prompted for the name of the image file to be
	** examined, or the image file name can be provided as an argument
	** to the command.
	**
	** File names provided do not need to include the extension if
	** the image file extension is one of the standard strings common
	** to Truevision TGA image file names ( e.g., TGA, WIN, VST, VDA, ICB )
	*/
	if ( argc == 1 )
	{
		puts( versionStr );
		printf( "Enter name of file to examine: " );
		gets( fileName );
		if ( strlen( fileName ) == 0 ) exit( 0 );
		fileCount = 1;
	}
	else
	{
		fileCount = ParseArgs( argc, argv );
		if ( fileCount == 0 ) exit( 0 );
		argv++;
		while ( **argv == '-' ) argv++; 
		strcpy( fileName, *argv );
	}
	for ( files = 0; files < fileCount; ++files )
	{
		if ( files != 0 )
		{
			argv++;
			while ( **argv == '-' ) argv++; 
			strcpy( fileName, *argv );
		}
		/*
		** See if we can find the file as specified or with one of the
		** standard filename extensions...
		*/
		fileFound = 0;
		if ( stat( fileName, &statbuf ) == 0 ) fileFound = 1;
		else
		{
			/*
			** If there is already an extension specified, skip
			** the search for standard extensions
			*/
			q = strchr( fileName, '.' );
			if ( q != NULL )
			{
				q = fileName + strlen( fileName );
			}
			else
			{
				i = 0;
				strcat( fileName, extNames[i] );
				q = strchr( fileName, '.' );
				while ( extNames[i] != NULL )
				{
					strcpy( q, extNames[i] );
					if ( stat( fileName, &statbuf ) == 0 )
					{
						fileFound = 1;
						break;
					}
					++i;
				}
			}
		}
		if ( fileFound )
		{
			_clearscreen( _GCLEARSCREEN );
			printf( "Processing TGA File: %s\n", fileName );
			fp = fopen( fileName, "rb" );
			/*
			** It would be nice to be able to read in the entire
			** structure with one fread, but compiler dependent
			** structure alignment precludes the simplistic approach.
			** Instead, fill each field individually, and use routines
			** that will allow code to execute on various hosts by
			** recompilation with particular compiler flags.
			**
			** Start by reading the fields associated with the original
			** TGA format.
			*/
			f.idLength = ReadByte( fp );
			f.mapType = ReadByte( fp );
			f.imageType = ReadByte( fp );
			f.mapOrigin = ReadShort( fp );
			f.mapLength = ReadShort( fp );
			f.mapWidth = ReadByte( fp );
			f.xOrigin = ReadShort( fp );
			f.yOrigin = ReadShort( fp );
			f.imageWidth = ReadShort( fp );
			f.imageHeight = ReadShort( fp );
			f.pixelDepth = ReadByte( fp );
			f.imageDesc = ReadByte( fp );
			memset( f.idString, 0, 256 );
			if ( f.idLength > 0 )
			{
				fread( f.idString, 1, f.idLength, fp );
			}
			/*
			** Now see if the file is the new (extended) TGA format.
			*/
			if ( !fseek( fp, statbuf.st_size - 26, SEEK_SET ) )
			{
				f.extAreaOffset = ReadLong( fp );
				f.devDirOffset = ReadLong( fp );
				fgets( f.signature, 18, fp );
				if ( strcmp( f.signature, "TRUEVISION-XFILE." ) )
				{
					/*
					** Reset offset values since this is not a new TGA file
					*/
					f.extAreaOffset = 0L;
					f.devDirOffset = 0L;
					
					strcpy( outFileName, fileName );
					i = strlen( fileName );
					outFileName[ i - 3 ] = '\0';	/* remove extension */
					strcat( outFileName, "$$$" );
					if ( ( outFile = fopen( outFileName, "wb" ) ) != NULL )
					{
						if ( OutputTGAFile( fp, outFile, &f ) < 0 )
						{
							fclose( outFile );
							unlink( outFileName );
						}
						else
						{
							fclose( outFile );
							fclose( fp );
							fp = (FILE *)0;
							unlink( fileName );
							rename( outFileName, fileName );
						}
					}
					else
					{
						puts( "Unable to create output file." );
					}
				}
				else puts( "Input file must be original TGA format." );
			}
			else
			{
				puts( "Error seeking to end of file for possible extension data." );
			}
			if ( fp != NULL ) fclose( fp );
		}
		else
		{
			*q = '\0';
			printf("Unable to open image file %s\n", fileName );
		}
	}
}



/*
** Count pixels in buffer until two identical adjacent ones found
*/

int
CountDiffPixels( p, bpp, pixCnt )
char	*p;
int		bpp;
int		pixCnt;
{
	unsigned long	pixel;
	unsigned long	nextPixel;
	int				n;

	n = 0;
	if ( pixCnt == 1 ) return( pixCnt );
	pixel = GetPixel( p, bpp );
	while ( pixCnt > 1 )
	{
		p += bpp;
		nextPixel = GetPixel( p, bpp );
		if ( nextPixel == pixel ) break;
		pixel = nextPixel;
		++n;
		--pixCnt;
	}
	if ( nextPixel == pixel ) return( n );
	return( n + 1 );
}



long
CountRLEData( fp, x, y, bytesPerPixel )
FILE			*fp;
unsigned int	x;
unsigned int	y;
int				bytesPerPixel;
{
	long			n;
	long			pixelCount;
	long			totalPixels;
	unsigned int	value;

	n = 0L;
	pixelCount = 0L;
	totalPixels = (long)x * (long)y;

	while ( pixelCount < totalPixels )
	{
		value = (unsigned int)ReadByte( fp );
		n++;
		if ( value & 0x80 )
		{
			n += bytesPerPixel;
			pixelCount += (value & 0x7f) + 1;
			if ( fread( copyBuf, 1, bytesPerPixel, fp ) != bytesPerPixel )
			{
				puts( "Error counting RLE data." );
				return( 0L );
			}
		}
		else
		{
			value++;
			n += value * bytesPerPixel;
			pixelCount += value;
			if ( fread( copyBuf, bytesPerPixel, value, fp ) != value )
			{
				puts( "Error counting raw data." );
				return( 0L );
			}
		}
	}
	return( n );
}



int
CountSamePixels( p, bpp, pixCnt )
char	*p;
int		bpp;
int		pixCnt;
{
	unsigned long	pixel;
	unsigned long	nextPixel;
	int				n;

	n = 1;
	pixel = GetPixel( p, bpp );
	pixCnt--;
	while ( pixCnt > 0 )
	{
		p += bpp;
		nextPixel = GetPixel( p, bpp );
		if ( nextPixel != pixel ) break;
		++n;
		--pixCnt;
	}
	return( n );
}




int
DisplayImageData( q, n, bpp )
unsigned char *q;
int	n;
int	bpp;
{
	long i;
	int j;
	unsigned char a, b, c;

	i = 0;
	while ( i < n ) 
	{
		printf( "%08lX: ", i );
		switch ( bpp )
		{
		case 4:
			for ( j = 0; j < 4; ++j )
			{
				printf( "%08lx ", *(unsigned long *)q );
				q += 4;
			}
			i += 16;
			break;
		case 3:
			for ( j = 0; j < 8; ++j )
			{
				a = *q++;
				b = *q++;
				c = *q++;
				printf( "%02x%02x%02x ", c, b, a );
			}
			i += 24;
			break;
		case 2:
			for ( j = 0; j < 8; ++j )
			{
				printf( "%04x ", *(unsigned int *)q );
				q += 2;
			}
			i += 16;
			break;
		default:
			for ( j = 0; j < 16; ++j )
			{
				printf( "%02x ", *(unsigned char *)q++ );
			}
			i += 16;
			break;
		}
		putchar( '\n' );
	}
	return( 0 );
}



/*
** Retrieve a pixel value from a buffer.  The actual size and order
** of the bytes is not important since we are only using the value
** for comparisons with other pixels.
*/

unsigned long
GetPixel( p, bpp )
unsigned char	*p;
int				bpp;		/* bytes per pixel */
{
	unsigned long	pixel;

	pixel = (unsigned long)*p++;
	while ( bpp-- > 1 )
	{
		pixel <<= 8;
		pixel |= (unsigned long)*p++;
	}
	return( pixel );
}



int
OutputTGAFile( ifp, ofp, sp )
FILE		*ifp;		/* input file pointer */
FILE		*ofp;		/* output file pointer */
TGAFile		*sp;		/* output TGA structure */
{
	long			byteCount;
	unsigned long	fileOffset;
	int				i;
	int				bytesPerPixel;
	int				bCount;
	int				rleCount;
	unsigned char	*imageBuff;
	unsigned char	*packBuff;
	unsigned char	outType;
	unsigned char	outDepth;
	unsigned char	outDesc;

	/*
	** First, we need to determine what operation is to be performed.
	** We could be run length encoding an uncompressed image, or
	** we could be uncompressing an RLE image.  Or we could be compacting
	** a 32 bit image to a 24 bit image.
	*/
	if ( noAlpha )
	{
		if ( sp->pixelDepth != 32 || sp->imageType != 2 )
		{
			puts( "Image file must be in 32 bit uncompressed format." );
			return( -1 );
		}
		else
		{
			outDepth = 24;
			outType = sp->imageType;
			outDesc = sp->imageDesc & 0xf0;
		}
	}
	else if ( !unPack && sp->imageType > 0 &&  sp->imageType < 4 )
	{
		outType = sp->imageType + 8;
		outDepth = sp->pixelDepth;
		outDesc = sp->imageDesc;
	}
	else if ( unPack && sp->imageType > 8 && sp->imageType < 12 )
	{
		outType = sp->imageType - 8;
		outDepth = sp->pixelDepth;
		outDesc = sp->imageDesc;
	}
	else
	{
		puts( "File type is inconsistent with requested operation." );
		return( -1 );
	}
	/*
	** The output file was just opened, so the first data
	** to be written is the standard header based on the
	** original TGA specification.
	*/
	if ( WriteByte( sp->idLength, ofp ) < 0 ) return( -1 );
	if ( WriteByte( sp->mapType, ofp ) < 0 ) return( -1 );
	if ( WriteByte( outType, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->mapOrigin, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->mapLength, ofp ) < 0 ) return( -1 );
	if ( WriteByte( sp->mapWidth, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->xOrigin, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->yOrigin, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->imageWidth, ofp ) < 0 ) return( -1 );
	if ( WriteShort( sp->imageHeight, ofp ) < 0 ) return( -1 );
	if ( WriteByte( outDepth, ofp ) < 0 ) return( -1 );
	if ( WriteByte( outDesc, ofp ) < 0 ) return( -1 );
	if ( sp->idLength )
	{
		if ( WriteStr( sp->idString, sp->idLength, ofp ) < 0 )
			return( -1 );
	}
	/*
	** Now we need to copy the color map data from the input file
	** to the output file.
	*/
	byteCount = 18 + sp->idLength;
	if ( fseek( ifp, byteCount, SEEK_SET ) != 0 ) return( -1 );
	byteCount = ((sp->mapWidth + 7) >> 3) * (long)sp->mapLength;
	fileOffset = 18 + sp->idLength + byteCount;
	while ( byteCount > 0 )
	{
		if ( byteCount - CBUFSIZE < 0 )
		{
			fread( copyBuf, 1, (int)byteCount, ifp );
			if ( fwrite( copyBuf, 1, (int)byteCount, ofp ) != (int)byteCount )
				return( -1 );
		}
		else
		{
			fread( copyBuf, 1, CBUFSIZE, ifp );
			if ( fwrite( copyBuf, 1, CBUFSIZE, ofp ) != CBUFSIZE )
				return( -1 );
		}
		byteCount -= CBUFSIZE;
	}
	/*
	** Now process the image data.
	*/
	bytesPerPixel = (sp->pixelDepth + 7) >> 3;
	bCount = sp->imageWidth * bytesPerPixel;
	if ( (imageBuff = malloc( bCount )) == NULL )
	{
		puts( "Unable to allocate image buffer" );
		return( -1 );
	}

	if ( noAlpha )
	{
		for ( i = 0; i < sp->imageHeight; ++i )
		{
			if ( fread( imageBuff, 1, bCount, ifp ) != bCount )
			{
				puts( "Error reading uncompressed data." );
				free( imageBuff );
				return( -1 );
			}
			StripAlpha( imageBuff, bCount );
			if ( fwrite( imageBuff, 1, bCount - sp->imageWidth, ofp ) !=
					bCount - sp->imageWidth )
			{
				puts( "Error writing 24 bit image data." );
				free( imageBuff );
				return( -1 );
			}
		}
	}
	else if ( !unPack )
	{
		if ( (packBuff = malloc( sp->imageWidth * (bytesPerPixel + 1) )) == NULL )
		{
			puts( "Error allocating encoded buffer." );
			free( imageBuff );
			return( -1 );
		}
		for ( i = 0; i < sp->imageHeight; ++i )
		{
			if ( fread( imageBuff, 1, bCount, ifp ) != bCount )
			{
				puts( "Error reading uncompressed data." );
				free( imageBuff );
				free( packBuff );
				return( -1 );
			}
			rleCount = RLEncodeRow( imageBuff, packBuff, sp->imageWidth,
							bytesPerPixel );
			if ( fwrite( packBuff, 1, rleCount, ofp ) != rleCount )
			{
				puts( "Error writing RLE image data." );
				free( imageBuff );
				free( packBuff );
				return( -1 );
			}
		}
		free( packBuff );
	}
	else
	{
		/*
		** Uncompress image data
		*/
		for ( i = 0; i < sp->imageHeight; ++i )
		{
			if ( ReadRLERow( imageBuff, bCount, bytesPerPixel, ifp ) < 0 )
			{
				puts("Error reading RLE data." );
				free( imageBuff );
				return( -1 );
			}
			if ( fwrite( imageBuff, 1, bCount, ofp ) != bCount )
			{
				puts("Error writing uncompressed data." );
				free( imageBuff );
				return( -1 );
			}
		}
	}
	free( imageBuff );

	return( 0 );
}



int
ParseArgs( argc, argv )
int		argc;
char	**argv;
{
	int		i;
	int		n;
	char	*p;

	n = 0;
	for ( i = 1; i < argc; ++i )
	{
		p = *(++argv);
		if ( *p == '-' )
		{
			p++;
			if ( stricmp( p, "unpack" ) == 0 ) unPack = 1;
			else if ( stricmp( p, "32to24" ) == 0 ) noAlpha = 1;
			else if ( stricmp( p, "version" ) == 0 )
			{
				puts( versionStr );
				exit( 0 );
			}
			else
			{
				puts( "Usage: tgapack [options] [file1] [file2...]" );
				puts( "  where options can be:" );
				puts( "    -unpack\t\tuncompress image data" );
				puts( "    -32to24\t\tconvert 32 bit image to 24 bit image" );
				puts( "    -version\t\treport version number" );
				exit( 0 );
			}
		}
		else ++n;
	}
	return( n );
}




void
PrintImageType( Itype )
register int Itype;
{
	if ( Itype > 255 || Itype < 0 )
	{
		puts("Illegal/Undefined Image Type");
		return;
	}

	if ( Itype > 127 )
	{
		puts("Unknown Image Type - Application Specific");
		return;
	}

	switch (Itype)
	{
	case 0:
		puts("Unknown Image Type - No Image Data Present");
		break;
	case 1:
		puts("Uncompressed Color Mapped Image (e.g., VDA/D, TARGA M8)");
		break;
	case 2:
		puts("Uncompressed True Color Image (e.g., ICB, TARGA 16/24/32)");
		break;
	case 3:
		puts("Uncompressed Black & White Image (e.g., TARGA 8/M8)");
		break;
	case 9:
		puts("Run Length Encoded Color Mapped Image (e.g., VDA/D, TARGA M8)");
		break;
	case 10:
		puts("Run Length Encoded True Color Image (e.g., ICB, TARGA 16/24/32)");
		break;
	case 11:
		puts("Compressed Black & White Image (e.g., TARGA 8/M8)");
		break;
	case 32:
	case 34:
		puts("Compressed (Huffman/Delta/RLE) Color Mapped Image (e.g., VDA/D) - Obsolete");
		break;
	case 33:
	case 35:
		puts("Compressed (Huffman/Delta/RLE) Color Mapped Four Pass Image (e.g., VDA/D) - Obsolete");
		break;
	default:
		puts("Unknown Image Type");
		break;
	}
}




void
PrintTGAInfo( sp )
TGAFile *sp;		/* TGA structure pointer */
{
	int	i;

	printf("ID Field Length          = %3d\n", sp->idLength);

	printf("Color Map Type           = %3d  (Color Map Data is ", sp->mapType);
	if (sp->mapType) puts("Present)");
	else puts("Absent)");  

	printf("Image Type               = %3d\n  ", sp->imageType);
	PrintImageType( sp->imageType );

	printf("Color Map Origin         = 0x%04x (%5d)",
		sp->mapOrigin, sp->mapOrigin);
	puts( "  (First Index To Be Loaded)" );
	printf("Color Map Length         = 0x%04x (%5d)\n",
		sp->mapLength,sp->mapLength);
	printf("Color Map Entry Size     = %6d\n", sp->mapWidth);

	printf("Image X-Origin, Y-Origin =  %05d, %05d\n",
		sp->xOrigin, sp->yOrigin);
	printf("Image Width, Height      =  %05d, %05d\n",
			sp->imageWidth, sp->imageHeight);

	printf("Image Pixel Depth        = 0x%04x (%05d)\n",
		sp->pixelDepth, sp->pixelDepth);
	printf("Image Descriptor         = 0x%04x\n", sp->imageDesc);
	printf("  %d Attribute Bits Per Pixel\n", sp->imageDesc & 0xf );
	printf("  First Pixel Destination is ");

	i = (sp->imageDesc & 0x30) >> 4;
	puts( orientStr[i] );

	i = (sp->imageDesc & 0xc0) >> 6;
	if ( i > 0 && i < 3 ) puts( interleaveStr[i - 1] );

	if ( sp->idLength )
	{
		printf( "Image ID:\n  " );
		puts( f.idString );
	}
}


UINT8
ReadByte( fp )
FILE *fp;
{
	UINT8	value;

#if MSDOS
	fread( &value, 1, 1, fp );
#else
#endif
	return( value );
}


void
ReadCharField( fp, p, n )
FILE	*fp;
char	*p;
int		n;
{
	while ( n )
	{
		*p++ = (char)fgetc( fp );	/* no error check, no char conversion */
		--n;
	}
}




UINT32
ReadLong( fp )
FILE *fp;
{
	UINT32	value;

#if MSDOS
	fread( &value, 4, 1, fp );
#else
#endif
	return( value );
}



int
ReadRLERow( p, n, bpp, fp )
unsigned char	*p;
int		n;			/* buffer size in bytes */
int		bpp;		/* bytes per pixel */
FILE	*fp;
{
	unsigned int			value;
	int						i;
	static unsigned char	*q;

	while ( n > 0 )
	{
		if ( inRLEPacket )
		{
			if ( packetSize * bpp > n )
			{
				value = n / bpp;		/* calculate pixel count */
				packetSize -= value;
				n = 0;
			}
			else
			{
				n -= packetSize * bpp;
				value = packetSize;
				packetSize = 0;
				inRLEPacket = 0;
			}
			while ( value > 0 )
			{
				*p++ = rleBuf[0];
				if ( bpp > 1 ) *p++ = rleBuf[1];
				if ( bpp > 2 ) *p++ = rleBuf[2];
				if ( bpp > 3 ) *p++ = rleBuf[3];
				value--;
			}
		}
		else if ( inRawPacket )
		{
			if ( packetSize * bpp > n )
			{
				value = n;
				packetSize -= n / bpp;
				n = 0;
			}
			else
			{
				value = packetSize * bpp;	/* calculate byte count */
				n -= value;
				inRawPacket = 0;
			}
			for ( i = 0; i < value; ++i ) *p++ = *q++;
		}
		else
		{
			/*
			** No accumulated data in buffers, so read from file
			*/
			packetSize = (unsigned int)ReadByte( fp );
			if ( packetSize & 0x80 )
			{
				packetSize &= 0x7f;
				packetSize++;
				if ( packetSize * bpp > n )
				{
					value = n / bpp;		/* calculate pixel count */
					packetSize -= value;
					inRLEPacket = 1;
					n = 0;
				}
				else
				{
					n -= packetSize * bpp;
					value = packetSize;
				}
				if ( fread( rleBuf, 1, bpp, fp ) != bpp ) return( -1 );
				while ( value > 0 )
				{
					*p++ = rleBuf[0];
					if ( bpp > 1 ) *p++ = rleBuf[1];
					if ( bpp > 2 ) *p++ = rleBuf[2];
					if ( bpp > 3 ) *p++ = rleBuf[3];
					value--;
				}
			}
			else
			{
				packetSize++;
				/*
				** Maximum for packetSize is 128 so as long as RLEBUFSIZ
				** is at least 512, and bpp is not greater than 4
				** we can read in the entire raw packet with one operation.
				*/
				if ( fread( rleBuf, bpp, packetSize, fp ) != packetSize )
					return( -1 );
				/*
				** But is there enough room to copy them to our line buffer?
				*/
				if ( packetSize * bpp > n )
				{
					value = n;				/* number of bytes remaining */
					packetSize -= n / bpp;
					inRawPacket = 1;
					n = 0;
				}
				else
				{
					value = packetSize * bpp;	/* calculate byte count */
					n -= value;
				}
				for ( i = 0, q = rleBuf; i < value; ++i ) *p++ = *q++;
			}
		}
	}
	return( 0 );
}




UINT16
ReadShort( fp )
FILE *fp;
{
	UINT16	value;

#if MSDOS
	fread( &value, 2, 1, fp );
#else
#endif
	return( value );
}



int
RLEncodeRow( p, q, n, bpp )
char	*p;			/* data to be encoded */
char	*q;			/* encoded buffer */
int		n;			/* number of pixels in buffer */
int		bpp;		/* bytes per pixel */
{
	int				diffCount;		/* pixel count until two identical */
	int				sameCount;		/* number of identical adjacent pixels */
	int				RLEBufSize;		/* count of number of bytes encoded */

	RLEBufSize = 0;
	while ( n > 0 )
	{
		diffCount = CountDiffPixels( p, bpp, n );
		sameCount = CountSamePixels( p, bpp, n );
		if ( diffCount > 128 ) diffCount = 128;
		if ( sameCount > 128 ) sameCount = 128;
		if ( diffCount > 0 )
		{
			/* create a raw packet */
			*q++ = (char)(diffCount - 1);
			n -= diffCount;
			RLEBufSize += (diffCount * bpp) + 1;
			while ( diffCount > 0 )
			{
				*q++ = *p++;
				if ( bpp > 1 ) *q++ = *p++;
				if ( bpp > 2 ) *q++ = *p++;
				if ( bpp > 3 ) *q++ = *p++;
				diffCount--;
			}
		}
		if ( sameCount > 1 )
		{
			/* create a RLE packet */
			*q++ = (char)((sameCount - 1) | 0x80);
			n -= sameCount;
			RLEBufSize += bpp + 1;
			p += (sameCount - 1) * bpp;
			*q++ = *p++;
			if ( bpp > 1 ) *q++ = *p++;
			if ( bpp > 2 ) *q++ = *p++;
			if ( bpp > 3 ) *q++ = *p++;
		}
	}
	return( RLEBufSize );
}



char *
SkipBlank( p )
char	*p;
{
	while ( *p != '\0' && (*p == ' ' || *p == '\t') ) ++p;
	return( p );
}



void
StripAlpha( s, n )
unsigned char	*s;
int				n;
{
	int				i;
	unsigned char	*p;

	/*
	** Copy the RGB components omitting the alpha.  Perform the
	** copy in its own buffer.  This algorithm is probably
	** specific to the 80x86 since byte orderring will be different
	** on a 680x0 processor.
	*/
	p = s;
	for ( i = 0; i < n; ++i )
	{
		if ( ( i % 4) == 3 ) ++p;
		else *s++ = *p++;
	}
}


int
WriteByte( uc, fp )
UINT8	uc;
FILE	*fp;
{
#ifdef MSDOS
	if ( fwrite( &uc, 1, 1, fp ) == 1 ) return( 0 );
#else
#endif
	return( -1 );
}



int
WriteLong( ul, fp )
UINT32	ul;
FILE	*fp;
{
#ifdef MSDOS
	if ( fwrite( &ul, 4, 1, fp ) == 1 ) return( 0 );
#else
#endif
	return( -1 );
}


int
WriteShort( us, fp )
UINT16	us;
FILE	*fp;
{
#ifdef MSDOS
	if ( fwrite( &us, 2, 1, fp ) == 1 ) return( 0 );
#else
#endif
	return( -1 );
}


int
WriteStr( p, n, fp )
char	*p;
int		n;
FILE	*fp;
{
#ifdef MSDOS
	if ( fwrite( p, 1, n, fp ) == n ) return( 0 );
#else
#endif
	return( -1 );
}
