#include "Script.hpp"
//===========================================================================//
// File:	 GAF.cpp														 //
// Project:  GameOS					                                         //
// Contents: Script control													 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 07/02/98  JTR	Created													 //
//					Currently only supports 16 bit storage					 //
//---------------------------------------------------------------------------//
// Copyright (C) 2000, Microsoft, Inc.										//
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//
//#include "GAFCompressor.hpp"


extern HGOSHEAP gScriptHeap[];





void Crunch(DWORD type, char * lpSrc, char * lpDst, int Width, int Height);
unsigned char GetCompressionType(DWORD * data, int w, int h);
//////////////////////////////////////////////////////////////////////////////////
// Load a GAF from disk. Use %d in filename to load images
//////////////////////////////////////////////////////////////////////////////////
void gos_CreateGAFResource(HGOSGAF * gaf, char * filename)
{

	*gaf = new(gScriptHeap[4]) gos_GAF(filename);
}

//////////////////////////////////////////////////////////////////////////////////
// Retrieve the width, height, and number of frames
//////////////////////////////////////////////////////////////////////////////////
void gos_GetGAFSize(HGOSGAF gaf, int * x, int * y, int * length)
{
	if (length) *length = gaf->m_gafHeader->m_totalImages;
	if (x) *x = gaf->m_gafHeader->m_width;
	if (y) *y = gaf->m_gafHeader->m_height;
}

//////////////////////////////////////////////////////////////////////////////////
// Imprint the surf <dst> with the <frame>th image in GAF <gaf> at <x>, <y>
//////////////////////////////////////////////////////////////////////////////////
void gos_GetGAFFrame(HGOSGAF gaf, int frame, HGOSSURFACE * dst)
{
	gaf->GetFrame(frame, dst);
}

//////////////////////////////////////////////////////////////////////////////////
// Deallocate all memory associated with the GAF <gaf>.
//////////////////////////////////////////////////////////////////////////////////
void gos_DestroyGAFResource(HGOSGAF * gaf)
{
	gos_PushCurrentHeap(gScriptHeap[4]);
	delete *gaf;
	gos_PopCurrentHeap();
	*gaf = NULL;
}

//////////////////////////////////////////////////////////////////////////////////
// Compress a frame relative to the first frame in series (as RLE delta)
//////////////////////////////////////////////////////////////////////////////////

int gos_GAF::CompressGAFFrameFromFirst(HGOSFILE dst_file, char * path, int frame, DWORD * lpOriginalData, DWORD * lpFrameData, DWORD * lpDeltaData, int size)
{
	char file[128];
	dst_file = dst_file;
	sprintf(file, path,frame);
	HGOSSURFACE dsurf = new(gScriptHeap[4]) Surface(file,1);
	gos_GAF::Squish((char *) lpFrameData, (char *) dsurf->m_nativeFormat.m_lpDynData, dsurf->m_nativeFormat.m_pitch, dsurf->m_nativeFormat.m_width, dsurf->m_nativeFormat.m_height);

	// start comparing the lpDeltaSurface with lpOriginalData
#define IDENTICAL 0x00000000
#define DIFFERENCES 0x01000000
#define DIFFERENCES_RUN 0x02000000

	bool done = false;
	int mode = IDENTICAL;
	int count = 0;
	int image_index = 0;

	DWORD * last_mode_addr;
	DWORD * lpOriginal = (DWORD *)lpDeltaData;

	size /= 4;

	while (!done)
	{
		last_mode_addr = (DWORD *) lpDeltaData;
		lpDeltaData += 1; // in words 2, in bytes 4

		mode = IDENTICAL;
		while(image_index < size && lpFrameData[image_index] == lpOriginalData[image_index] )
		{
			count ++;
			image_index++;
		}
		if (count>1)
		{
			*last_mode_addr = (DWORD) count;
			SPEW((GROUP_GAF,"COMP: Frame#%d: Same Count = %d at Offset %d",frame, count, (char *)last_mode_addr - (char *)lpOriginal ));
			count = 0;
			if (image_index == size)
			{
				lpDeltaData += 1;
				done = true;
			}
			continue;
		}
		else
		{
			mode = DIFFERENCES;
			image_index -= count;
			for(int i=0; i < count; i++)
			{
				*lpDeltaData = lpFrameData[image_index];
				lpDeltaData++;
				image_index++;
			}
			// bytes were different from original or there was a string of 
			// identical values to short to make compression worthwhile
			while(image_index < size && lpFrameData[image_index] != lpOriginalData[image_index] )
			{
				count += 1;
				*lpDeltaData = lpFrameData[image_index];
				lpDeltaData++;
				image_index++;
			}

			SPEW((GROUP_GAF,"COMP: Frame#%d: Diff Count = %d at Offset %d",frame, count, (char *)last_mode_addr - (char *)lpOriginal ));
			*last_mode_addr = (DWORD) count + 0x01000000;
			count = 0;
			if (image_index == size)
			{
				lpDeltaData += 1;
				done = true;
			}
			continue;
		}
	}

	
	// delete the delta surface; we don't need it anymore!
//
//  from last frame compression
//	memcpy(lpOriginalData,lpFrameData,dsurf->m_nativeFormat.m_width*4 * dsurf->m_nativeFormat.m_height);
	delete dsurf;
	SPEW((GROUP_GAF,"COMP: Frame#%d, Size = %d", frame, (char *)lpDeltaData-(char *)lpOriginal));
	return ((char *)lpDeltaData - (char *) lpOriginal)-4;
}


gos_GAF::gos_GAF(char * filename)
{
	// read in the file
	DWORD filesize;

	gos_PushCurrentHeap(gScriptHeap[4]);
	gos_GetFile(filename, &m_fileData, &filesize );
	gos_PopCurrentHeap();

	// create the header and surface
	m_gafHeader = (GAFHEADER *) m_fileData;

	switch (m_gafHeader->m_version)
	{
		case 1:
			{
				SPEW((GROUP_GAF, "loaded %s as type 1 (16b 555) gaf", filename));
				m_primarySurface = new(gScriptHeap[4]) Surface(m_gafHeader->m_width, m_gafHeader->m_height);
				m_primarySurface->m_isGAF = true;
				
				m_lastFrame = -1; // set the last frame read equal to the first (init)
				// load the raw data
				int size = m_gafHeader->m_width * m_gafHeader->m_height;
				
				DWORD *dest = (DWORD *) (m_primarySurface->m_nativeFormat.m_lpRawData);
				WORD *src = (WORD *) m_fileData+ (sizeof(GAFHEADER)/2);
				for(int i= 0; i < size; i++) 
				{
					WORD color = (WORD) *src++;
					BYTE red = (BYTE)(((color & 0x7f00) >> 10) <<3);
					BYTE grn = (BYTE)(((color & 0x03c0) >> 5)<<3);
					BYTE blu = (BYTE)((color &0x1f) <<3);
					*(dest++) = (red<<16)+(grn<<8)+blu + 0xff000000;
				}
						
				m_primarySurface->m_lpPath = (char *) gos_Malloc(strlen(filename)+1, gScriptHeap[4]);
				strcpy(m_primarySurface->m_lpPath,filename);
				
				m_tableOffset = (DWORD) m_fileData + (filesize - (m_gafHeader->m_totalImages *4))+4;
				GetFrame(0, &m_primarySurface);
			}
			break;
		case 2:
		{
			SPEW((GROUP_GAF, "loaded %s as type 2 gaf", filename));
			m_primarySurface = new(gScriptHeap[4]) Surface(m_gafHeader->m_width, m_gafHeader->m_height);
			m_primarySurface->m_isGAF = true;
			
			m_lastFrame = -1; // set the last frame read equal to the first (init)
			
			// load the raw data
			memcpy(m_primarySurface->m_nativeFormat.m_lpRawData, m_fileData+ sizeof(GAFHEADER), (m_gafHeader->m_width*4) * m_gafHeader->m_height);
			m_primarySurface->m_lpPath = (char *) gos_Malloc(strlen(filename)+1, gScriptHeap[4]);
			strcpy(m_primarySurface->m_lpPath,filename);
			
			m_tableOffset = (DWORD) m_fileData + (filesize - (m_gafHeader->m_totalImages *4))+4;
			GetFrame(0, &m_primarySurface);
			break;
		}
		case 4:
		{
			SPEW((GROUP_GAF, "loaded %s as type 4 gaf", filename));
			m_primarySurface = new(gScriptHeap[4]) Surface(m_gafHeader->m_width, m_gafHeader->m_height);
			m_primarySurface->m_isGAF = true;
			
			m_lastFrame = -1; // set the last frame read equal to the first (init)
			
			// load the raw data
			memcpy(m_primarySurface->m_nativeFormat.m_lpRawData, m_fileData+ sizeof(GAFHEADER), (m_gafHeader->m_width*4) * m_gafHeader->m_height);
			m_primarySurface->m_lpPath = (char *) gos_Malloc(strlen(filename)+1, gScriptHeap[4]);
			strcpy(m_primarySurface->m_lpPath,filename);
			
			m_tableOffset = (DWORD) m_fileData + (filesize - (m_gafHeader->m_totalImages *4))+4;
			GetFrame(0, &m_primarySurface);
			break;
		}
		case 5: // 4444
		case 6:	// 1555
		case 7: // 0565
		{
			SPEW((GROUP_GAF, "loaded %s as type 5 gaf", filename));
			m_primarySurface = new(gScriptHeap[4]) Surface(m_gafHeader->m_width, m_gafHeader->m_height);
			m_primarySurface->m_isGAF = true;
			
			m_lastFrame = -1; // set the last frame read equal to the first (init)
			
			// load the raw data
//			memcpy(m_primarySurface->m_nativeFormat.m_lpRawData, m_fileData+ sizeof(GAFHEADER), (m_gafHeader->m_width*4) * m_gafHeader->m_height);
			m_primarySurface->m_lpPath = (char *) gos_Malloc(strlen(filename)+1, gScriptHeap[4]);
			strcpy(m_primarySurface->m_lpPath,filename);
			
			m_tableOffset = (DWORD) m_fileData + (filesize - (m_gafHeader->m_totalImages *4))+4;
			GetFrame(0, &m_primarySurface);
			break;
		}
		default:
		{
			ScriptError("Bad GAF version: %s",filename);
		}

	}
}

void gos_GAF::GetFrame(int frame, HGOSSURFACE *targ)
{
	gosASSERT( m_gafHeader );
	gosASSERT( ((unsigned long ) frame) < m_gafHeader->m_totalImages);
	// same as last time? just return current
	if (frame == m_lastFrame)
	{
		*targ = m_primarySurface;
		return;
	}
	else
	{
		m_lastFrame = frame;
	}

	if (frame == 0)
	{
		char * src = (char *) m_primarySurface->m_nativeFormat.m_lpRawData;
		char * dst = (char *) m_primarySurface->m_nativeFormat.m_lpDynData;
		if (m_gafHeader->m_version == 1)
		{
			dst += ((m_gafHeader->m_width*4) * m_gafHeader->m_height);

			// what an awful storage format!
			// and have to freaking flip it! SLOOOOW!!
			for(unsigned long i=0; i < m_gafHeader->m_height; i++)
			{
				dst -= m_gafHeader->m_width*4;
				memcpy(dst,src,m_gafHeader->m_width*4);
				src += m_gafHeader->m_width*4;
			}
		}
		else
		if (m_gafHeader->m_version > 4 && m_gafHeader->m_version < 8)
		{
			int Height = m_gafHeader->m_height;
			int Width = m_gafHeader->m_width;
			DWORD * dst = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
			WORD * src = (WORD*)m_fileData+(sizeof(GAFHEADER)/2);
			memcpy(dst, src, Height*(Width*4));
		}
		else
		{
			memcpy(dst,src, (m_gafHeader->m_width*4)*m_gafHeader->m_height);
		}
		*targ = m_primarySurface;
		return;
	}

	if (m_gafHeader->m_version == 2)
	{
		// not that easy, get the delta and apply
	
		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		DWORD * newFrameStart = newFrame;
		DWORD size = ((DWORD )((DWORD *)m_tableOffset)[frame-2]) - ((DWORD)((DWORD *)m_tableOffset)[frame-3]) ;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
	
	
		DWORD count = 0;
	
		SPEW((GROUP_GAF,"DECOMP: Frame#%d: Frame Offset = %d, Size = %d", frame, ((DWORD *)delta)[0], size));
	
		while (count < size)
		{
			if (!(((DWORD *)delta)[0] & 0x01000000))
			{
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Same Count = %d at Offset %d", frame, (((DWORD *)delta)[0]), count));
				memcpy(newFrame, m_fileData+ sizeof(GAFHEADER) + (((DWORD) newFrame) - ((DWORD) newFrameStart)), ((DWORD *)delta)[0]*4);
				count += 4;
				newFrame += (((DWORD *)delta)[0]);
				delta += 4;
			}
			else
			{
				int len =  (((DWORD *)delta)[0] & 0x00ffffff);
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Diff Count = %d at Offset %d", frame, len, count));
				count += 4;
				delta += 4;
				memcpy(newFrame,(char *) delta,len*4);
				newFrame += len;
				delta += len*4;
				count += len*4;
			}
		}
	}
	else
	if (m_gafHeader->m_version == 4)
	{
		// not that easy, get the delta and apply
	
		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		DWORD * newFrameStart = newFrame;
		DWORD size = ((DWORD )((DWORD *)m_tableOffset)[frame-2]) - ((DWORD)((DWORD *)m_tableOffset)[frame-3]) ;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
	
		DWORD count = 0;
	
		SPEW((GROUP_GAF,"DECOMP: Frame#%d: Frame Offset = %d, Size = %d", frame, ((DWORD *)delta)[0], size));
	
		while (count < size)
		{
			if ( ((char *)delta)[3] & 0x02)
			{
				int len =  (((DWORD *)delta)[0] & 0x00ffffff);
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Diff of Same Count = %d at Offset %d", frame, len, count));
				count += 4;
				delta += 4;
				for(int i = 0 ; i < len; i ++)
				{
					*newFrame = *((DWORD *)delta);
					newFrame ++;
				}
				delta += 4;
				count += 4;
			}
			else
			if (((char *)delta)[3] & 0x01)
			{
				int len =  (((DWORD *)delta)[0] & 0x00ffffff);
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Diff Count = %d at Offset %d", frame, len, count));
				count += 4;
				delta += 4;
				memcpy(newFrame,(char *) delta,len*4);
				newFrame += len;
				delta += len*4;
				count += len*4;
			}
			else
			{
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Same Count = %d at Offset %d", frame, (((DWORD *)delta)[0]), count));
				memcpy(newFrame, m_fileData+ sizeof(GAFHEADER) + (((DWORD) newFrame) - ((DWORD) newFrameStart)), ((DWORD *)delta)[0]*4);
				count += 4;
				newFrame += (((DWORD *)delta)[0]);
				delta += 4;
			}

		}
	}
	else
	if (m_gafHeader->m_version == 5)
	{
 		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		WORD * decFrame = (WORD *) m_primarySurface->m_nativeFormat.m_lpRawData;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
		DWORD src_len = *((DWORD*)delta);
		delta += 4;
		gos_LZDecompress((unsigned char *) decFrame, (unsigned char *) delta, src_len);

		int Count = m_gafHeader->m_height * m_gafHeader->m_width;
		for(int y = 0; y < Count; y++)
		{
			*newFrame = ( ((DWORD) ((*decFrame) &0xf000)) <<16) | 
				(( ((DWORD) ((*decFrame) &0x0f00)) <<4)<<8) |
				(( ((DWORD) ((*decFrame) &0x00f0)) <<4)<<4) | 
				( ((DWORD) ((*decFrame)  &0x000f)) <<4);
			newFrame++;
			decFrame++;
		}

		return;
	}
	else
	if (m_gafHeader->m_version == 6)
	{
 		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		WORD * decFrame = (WORD *) m_primarySurface->m_nativeFormat.m_lpRawData;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
		DWORD src_len = *((DWORD*)delta);
		delta += 4;
		gos_LZDecompress((unsigned char *) decFrame, (unsigned char *) delta, src_len);

		int Count = m_gafHeader->m_height * m_gafHeader->m_width;
		for(int y = 0; y < Count; y++)
		{
			DWORD alp = (0x8000 & (*decFrame)) ? 0xff000000 : 0;
			DWORD red = (((*decFrame) & 0x7c00) << 9);
			DWORD grn = (((*decFrame) & 0x03e0) << 6);
			DWORD blu = (((*decFrame) & 0x001f) << 3);
			*newFrame =alp + red + grn + blu;
			newFrame++;
			decFrame++;
		}
		return;
	}
	else
	if (m_gafHeader->m_version == 7)
	{
 		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		WORD * decFrame = (WORD *) m_primarySurface->m_nativeFormat.m_lpRawData;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
		DWORD src_len = *((DWORD*)delta);
		delta += 4;
		gos_LZDecompress((unsigned char *) decFrame, (unsigned char *) delta, src_len);

		int Count = m_gafHeader->m_height * m_gafHeader->m_width;
		for(int y = 0; y < Count; y++)
		{
			BYTE red = (BYTE)((((*decFrame) & 0xf800) >> 11) << 3);
			BYTE grn = (BYTE)((((*decFrame) & 0x07e0) >> 6) << 3);
			BYTE blu = (BYTE)((((*decFrame) & 0x001f)) << 3);
			*newFrame =0xff000000 + (red<<16) + (grn<<8) + blu;
			newFrame++;
			decFrame++;
		}

		return;
	}
	else
	{
		// not that easy, get the delta and apply
		DWORD * newFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpDynData;
		DWORD * origFrame = (DWORD *) m_primarySurface->m_nativeFormat.m_lpRawData;
		DWORD size = ((DWORD )((DWORD *)m_tableOffset)[frame-2]) - ((DWORD)((DWORD *)m_tableOffset)[frame-3]) ;
	
		DWORD delta = ((DWORD *)m_tableOffset)[frame-3];
		delta = delta +(DWORD)m_fileData;
	
	
		DWORD count = 0;
	
		SPEW((GROUP_GAF,"DECOMP: Frame#%d: Frame Offset = %d, Size = %d", frame, ((DWORD *)delta)[0], size));
	
		while (count < size)
		{
			if (!(((DWORD *)delta)[0] & 0x01000000))
			{
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Same Count = %d at Offset %d", frame, (((DWORD *)delta)[0]), count));
				memcpy(newFrame, origFrame, ((DWORD *)delta)[0]*4);
				newFrame += ((DWORD *)delta)[0];
				origFrame += ((DWORD *)delta)[0];


				count += 4;
				delta += 4;
			}
			else
			{
				int len =  (((DWORD *)delta)[0] & 0x00ffffff);
//				SPEW((GROUP_GAF,"DECOMP: Frame#%d: Diff Count = %d at Offset %d", frame, len, count));
				count += 4;
				delta += 4;
				WORD * pColor = ((WORD*)delta);
				for(int i=0; i < len; i++)
				{
					WORD color = pColor[i];
					BYTE red = (BYTE)(((color & 0x7f00) >> 10) << 3);
					BYTE grn = (BYTE)(((color & 0x03c0) >> 5) << 3);
					BYTE blu = (BYTE)(((color & 0x001f)) << 3);
					*newFrame++ =0xff000000 + (red<<16) + (grn<<8) + blu;
					origFrame++;
				}
				delta += len*2;
				count += len*2;
			}
		}
	MirrorVertical
		(
			(char *) m_primarySurface->m_nativeFormat.m_lpDynData,
			(char *) m_primarySurface->m_nativeFormat.m_lpDynData,
			m_primarySurface->m_nativeFormat.m_pitch,
			m_gafHeader->m_width,
			m_gafHeader->m_height
		);
	*targ = m_primarySurface;
	}
}

gos_GAF::~gos_GAF()
{
	delete m_primarySurface;
	m_primarySurface = NULL;
	gos_Free(m_fileData);
}



void gos_GAF::Squish(char * dst, char * src, DWORD p, DWORD x, DWORD y)
{
	x *= 4;
	for(unsigned long i=0; i < y; i++)
	{
		memcpy(dst,src, x);
		dst += p;
		src += p;
	}
}

void gos_GAF::MirrorVertical(char * dst, char * src, DWORD p, DWORD x, DWORD y)
{
	char data[4096];
	x *= 4;
	for(unsigned long i=0; i < y/2; i++)
	{
		memcpy(data,dst+(i*x), x);
		memcpy(dst+(i*x),src+(((y-1)-i)*p), x);
		memcpy(src+(((y-1)-i)*p), data, x);
	}
}

HGOSSURFACE gos_GetSurface(HGOSGAF gaf)
{
	return gaf->m_primarySurface;
}



//////////////////////////////////////////////////////////////////////////////////
// Build a GAF animation resource file from a list of Windows BMPs
// Accessed via ToolOS.hpp only!
//////////////////////////////////////////////////////////////////////////////////
int __stdcall gosScript_BuildGAFResource( char* orig_path, char* dst_name, gosGAF_Type gType)
{
	gType;
	int startFrame = 1;
	SPEW(( GROUP_GAF, "Build a GAF file..."));
	SPEW((GROUP_GAF,"%s, %s",orig_path,dst_name));

	GAFHEADER fileHeader;

	// load the first image in the series
	HGOSSURFACE surf1;
	char file[256];
	sprintf(file, strlwr(orig_path),0);
	if (!gos_DoesFileExist(file))
	{
		sprintf(file, strlwr(orig_path),1);
	}
	if (!gos_DoesFileExist(file))
		ScriptError("Cannot find file %s", file);

	// count the number of bitmaps
	DWORD total_images = 0;
	sprintf(file, strlwr(orig_path),0);
	if (gos_DoesFileExist(file))
	{
		startFrame = 0;
	}
	sprintf(file, orig_path, startFrame);
	int index = startFrame;
	while (gos_DoesFileExist(file))
	{
		total_images++;
		index++;
		sprintf(file, strlwr(orig_path), index);
	}

	sprintf(file, strlwr(orig_path),startFrame);
	surf1 = new(gScriptHeap[4]) Surface(file,1);

	// set the header
	fileHeader.m_hdr[0] = 'G'; fileHeader.m_hdr[1] = 'G'; fileHeader.m_hdr[2] = 'A'; 	fileHeader.m_hdr[3] = 'F';
	fileHeader.m_version = LATEST_GAF_VERSION;
	fileHeader.m_width = gos_GetSurfaceWidth(surf1);
	fileHeader.m_height = gos_GetSurfaceHeight(surf1);
	fileHeader.m_totalImages = total_images;
	fileHeader.m_pixelFormat = 3; // reserved, we don't make use of this yet

	// open a target file and fill it up with the header data
	HGOSFILE 
		dst_file;

	gos_OpenFile(&dst_file, dst_name, READWRITE);

	// allocate workspace
	DWORD * lpOriginalData = (DWORD *) gos_Malloc((fileHeader.m_width*4) * fileHeader.m_height,gScriptHeap[4]);
	DWORD * lpDeltaFrameData = (DWORD *) gos_Malloc(10000000, gScriptHeap[4]);
	DWORD * lpFlippedFrameData = (DWORD *) gos_Malloc(10000000, gScriptHeap[4]);

	lpOriginalData = (DWORD *) surf1->GetDataPointer();

	fileHeader.m_version = GetCompressionType(lpOriginalData, fileHeader.m_width, fileHeader.m_height);

	// write the header in the file;
	gos_WriteFile(dst_file,(char *) &fileHeader,sizeof(fileHeader));
	// write the entire uncompressed original in the file
	gos_WriteFile(dst_file,(char *) lpOriginalData, (fileHeader.m_width*4) * fileHeader.m_height);

	// remove the original; we don't need it anymore!

	// setup delta buffer and delta table offsets
	DWORD * delta_offset = (DWORD *) gos_Malloc((total_images+2) * sizeof(DWORD), gScriptHeap[4]);
	delta_offset[0] = sizeof(GAFHEADER);
	delta_offset[1] = delta_offset[0] + (fileHeader.m_width*4) * fileHeader.m_height;

	DWORD length;
	length = 0;

	DWORD total_length = 0;
	index = 1;
	for(unsigned long frame=startFrame; frame < total_images+startFrame-1; frame++)
	{
		char filen[128];
		sprintf(filen, strlwr(orig_path),frame+1);
		HGOSSURFACE dsurf = new(gScriptHeap[4]) Surface(filen,1);
		int length = dsurf->m_nativeFormat.m_height * (dsurf->m_nativeFormat.m_width*4);
		lpDeltaFrameData = (DWORD*) dsurf->GetDataPointer();
		Crunch(fileHeader.m_version, (char*)lpDeltaFrameData, (char*)lpOriginalData, dsurf->m_nativeFormat.m_width, dsurf->m_nativeFormat.m_height);
		int newlength = gos_LZCompress((unsigned char *) lpFlippedFrameData, (unsigned char *) lpOriginalData, length); 
		gos_WriteFile(dst_file,&length,4);
		gos_WriteFile(dst_file,(char *) lpFlippedFrameData, newlength);

		delta_offset[index+1] = delta_offset[index] + newlength+4;
		index++;
		total_length += newlength;
	}

	// write out delta offsets 
	gos_WriteFile(dst_file,(char *) delta_offset,sizeof(DWORD)*(total_images+2));

	SPEW((GROUP_GAF, "Total file size: %d", total_length ));

	delete surf1;
	gos_Free(lpFlippedFrameData);
	gos_Free(lpDeltaFrameData);
	gos_CloseFile(dst_file);
	return 0;
}

//////////////////////////////////////////////////////////////////////////////////
// Compress a frame relative to the first frame in series (as RLE delta)
//////////////////////////////////////////////////////////////////////////////////

int gos_GAF::CompressGAFFrameFromLast(HGOSFILE dst_file, char * path, int frame, DWORD * lpOriginalData, DWORD * lpFrameData, DWORD * lpDeltaData, int size)
{
	char file[128];
	dst_file = dst_file;
	sprintf(file, path,frame);
	HGOSSURFACE dsurf = new(gScriptHeap[4]) Surface(file,1);
	lpFrameData = (DWORD*) dsurf->GetDataPointer();

	// start comparing the lpDeltaSurface with lpOriginalData
	bool done = false;
	int mode = IDENTICAL;
	int count = 0;
	int image_index = 0;

	DWORD * last_mode_addr;
	DWORD * lpOriginal = (DWORD *)lpDeltaData;

	size /= 4;

	while (!done)
	{
		last_mode_addr = (DWORD *) lpDeltaData;
		lpDeltaData += 1; // in words 2, in bytes 4

		mode = IDENTICAL;
		while(image_index < size && lpFrameData[image_index] == lpOriginalData[image_index] )
		{
			count ++;
			image_index++;
		}
		if (count)
		{
			*last_mode_addr = (DWORD) count;
//			SPEW((GROUP_GAF,"COMP: Frame#%d: Same Count = %d at Offset %d",frame, count, (char *)last_mode_addr - (char *)lpOriginal ));
			count = 0;
			if (image_index == size)
			{
				lpDeltaData += 1;
				done = true;
			}
			continue;
		}
		else
		{
			if (image_index < size && lpFrameData[image_index] == lpFrameData[image_index+1])
			{
				mode = DIFFERENCES_RUN;

				*lpDeltaData = lpFrameData[image_index];
				while(image_index < size && lpFrameData[image_index] == lpFrameData[image_index+1] )
				{
					count += 1;
					image_index++;
				}
				*last_mode_addr = (DWORD) count + mode;

				if (image_index == size)
				{
					lpDeltaData += 1;
					done = true;
				}
				continue;
			}
			else
			{
				mode = DIFFERENCES;
				count++;
				*lpDeltaData = lpFrameData[image_index];
				lpDeltaData++;
				image_index++;
	
				// bytes were different from original or there was a string of 
				// identical values too short to make compression worthwhile
				while( image_index < size && lpFrameData[image_index] != lpOriginalData[image_index] )
				{
					count += 1;
					*lpDeltaData = lpFrameData[image_index];
					lpDeltaData++;
					image_index++;
				}
	
	//			SPEW((GROUP_GAF,"COMP: Frame#%d: Diff Count = %d at Offset %d",frame, count, (char *)last_mode_addr - (char *)lpOriginal ));
				*last_mode_addr = (DWORD) count + mode;
				count = 0;
				if (image_index == size)
				{
					lpDeltaData += 1;
					done = true;
				}
				continue;
			}
		}
	}

//	
//  from last frame compression
	delete dsurf;
	SPEW((GROUP_GAF,"COMP: Frame#%d, DeltaSize = %d", frame, (char *)lpDeltaData-(char *)lpOriginal));
	return ((char *)lpDeltaData - (char *) lpOriginal)-4;
}

void Crunch(DWORD type, char * lpSrc, char * lpDst, int Width, int Height)
{
	switch (type)
	{
	case 5:
		{
			DWORD * src = (DWORD *) lpSrc;
			WORD * dst = (WORD *) lpDst;
			for(int y = 0; y < Height; y++)
			{
				for(int x = 0; x < Width; x++)
				{
					DWORD v8888 = src[y*Width+x];
					
					DWORD alp = ((v8888 & 0xff000000) >> 24) / 16;
					DWORD red = ((v8888 & 0x00ff0000) >> 16) / 16;
					DWORD grn = ((v8888 & 0x0000ff00) >> 8) / 16;
					DWORD blu = (v8888 & 0x000000ff) / 16;
			
					WORD v4444 = ((WORD) ((alp<<12)+(red<<8)+(grn<<4)+blu));
					dst[y*Width+x] = v4444;
				}
			}
			break;
		}
	case 6:
		{
			DWORD * src = (DWORD *) lpSrc;
			WORD * dst = (WORD *) lpDst;
			for(int y = 0; y < Height; y++)
			{
				for(int x = 0; x < Width; x++)
				{
					DWORD v8888 = src[y*Width+x];
					
					DWORD alp = ((v8888 & 0xff000000)) ? 0x8000: 0;
					DWORD red = ((v8888 & 0x00ff0000) >> 16) / 8;
					DWORD grn = ((v8888 & 0x0000ff00) >> 8) / 8;
					DWORD blu = (v8888 & 0x000000ff) / 8;
			
					WORD v1555 = (WORD) (alp+(red<<10)+(grn<<5)+blu);
					dst[y*Width+x] = v1555;
				}
			}
			break;
		}
	case 7:
		{
			DWORD * src = (DWORD *) lpSrc;
			WORD * dst = (WORD *) lpDst;
			for(int y = 0; y < Height; y++)
			{
				for(int x = 0; x < Width; x++)
				{
					DWORD v8888 = src[y*Width+x];
					
					WORD red = (WORD) (((v8888 & 0x00ff0000) >> 16) / 8);
					WORD grn = (WORD) (((v8888 & 0x0000ff00) >> 8) / 4);
					WORD blu = (WORD) ((v8888 & 0x000000ff) / 8);
			
					WORD v565 = ((WORD) ((red<<11)+(grn<<5)+blu));
					dst[y*Width+x] = v565;
				}
			}
			break;
		}
	}
}

unsigned char GetCompressionType(DWORD * data, int w, int h)
{
	unsigned char type = 7;
	int count = (w)*h;
	for(int i = 0; i < count; i++)
	{
		int value = (*data) & 0xff000000;
		if (value == 0xff000000)
			data++;
		else
		if (value == 0x00000000 && (type == 7 || type == 6) )
		{
			type = 6;
			data++;
		}
		else
		{
			type = 5;
			break;
		}
	}
	return type;
}










