#include "chunk.hpp"
#include "chnktype.hpp"
#include "mishchnk.hpp"

#include "shpchunk.hpp"
#include "obchunk.hpp"



#include "huffman.hpp"


#include <mbstring.h>

#ifdef cencon
#define new my_new
#endif


// Class Lockable_Chunk_With_Children functions

#if cencon
#else
extern char * users_name;
#endif

//macro for helping to force inclusion of chunks when using libraries
FORCE_CHUNK_INCLUDE_IMPLEMENT(mishchnk)

BOOL Lockable_Chunk_With_Children::lock_chunk(File_Chunk & fchunk)
{
	if (!fchunk.filename) return FALSE;

	if (local_lock) return FALSE; // you can't lock a chunk twice

	#if DisableLock
	if(external_lock) return FALSE;
	local_lock = TRUE;
	set_lock_user (users_name);
	return TRUE;
	#else
	if (!fchunk.check_file()) return FALSE;
	if (updated_outside || external_lock) return FALSE;

	HANDLE rif_file;
	unsigned long bytes_read;

	int tries = 0;
	
 	rif_file = CreateFile (fchunk.filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
 					FILE_FLAG_RANDOM_ACCESS, 0);

	while(rif_file == INVALID_HANDLE_VALUE && tries<10)
	{
	 	rif_file = CreateFile (fchunk.filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
	 					FILE_FLAG_RANDOM_ACCESS, 0);
	 	tries ++;
		clock_t ctime = clock();
		double secs	= (double)ctime / (double)CLOCKS_PER_SEC;
		double secsgone;
		do
		{
			ctime = clock();
			secsgone = (double)ctime / (double)CLOCKS_PER_SEC;
		}
		while( (secsgone-secs)<1);
	}	
		
	if (rif_file == INVALID_HANDLE_VALUE) {
		return FALSE;
	}
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);
	
	List<int> chfptrs;
	list_chunks_in_file(& chfptrs, rif_file, identifier);

	LIF<int> cfpl(&chfptrs);

	if (chfptrs.size()) {
		for (; !cfpl.done(); cfpl.next()) {

			SetFilePointer (rif_file, cfpl(),0,FILE_BEGIN);

			if (file_equals(rif_file)) break;
		}
	}

	if (!cfpl.done()) {

		// go to start of chunk
		SetFilePointer (rif_file,cfpl(),0,FILE_BEGIN);
		
		// get header list
		if (get_head_id()) 
		{
			List<int> obhead;
			list_chunks_in_file(& obhead, rif_file, get_head_id());

			assert (obhead.size() == 1);

			int flags;

			// go to lock status in header
			SetFilePointer(rif_file,obhead.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			SetFilePointer(rif_file,-4,0,FILE_CURRENT);
			flags |= GENERAL_FLAG_LOCKED;
			WriteFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			WriteFile (rif_file, (long *) &users_name[0], 16, &bytes_read, 0);
		}

	}

	local_lock = TRUE;

	set_lock_user (users_name);

	CloseHandle (rif_file);
	return TRUE;
	#endif
	
}	

BOOL Lockable_Chunk_With_Children::unlock_chunk (File_Chunk & fchunk, BOOL updateyn)
{
	if (updateyn)	{
		updated = TRUE;
	}
	#if DisableLock
	local_lock = FALSE;
	(void)fchunk;
	#else
	else
	{
		fchunk.check_file();
		if (updated_outside || external_lock) return FALSE;

		HANDLE rif_file;
		unsigned long bytes_read;
	
		rif_file = CreateFile (fchunk.filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
							FILE_FLAG_RANDOM_ACCESS, 0);
		
		if (rif_file == INVALID_HANDLE_VALUE) {
			return FALSE;
		}
	
		SetFilePointer (rif_file,0,0,FILE_BEGIN);
		
		List<int> chfptrs;
		list_chunks_in_file(& chfptrs, rif_file, identifier);

		List<obinfile> obs;

		char name[50];

		LIF<int> ofpl(&chfptrs);

		if (chfptrs.size()) {
			for (; !ofpl.done(); ofpl.next()) {

				SetFilePointer (rif_file, ofpl(),0,FILE_BEGIN);

				if (file_equals(rif_file)) break;
			}
		}

		if (!ofpl.done()) {

			// go to start of chunk
			SetFilePointer (rif_file,ofpl(),0,FILE_BEGIN);
			
			// get header list
			if (get_head_id()) 
			{
				List<int> obhead;
				list_chunks_in_file(& obhead, rif_file, get_head_id());

				assert (obhead.size() == 1);

				int flags;

				// go to lock status in header
				SetFilePointer(rif_file,obhead.first_entry() + 12,0,FILE_BEGIN);
				ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
				SetFilePointer(rif_file,-4,0,FILE_CURRENT);
				flags &= ~GENERAL_FLAG_LOCKED;
				WriteFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
				WriteFile (rif_file, (long *) &users_name[0], 16, &bytes_read, 0);
			}

		}
		
		local_lock = FALSE;

		CloseHandle (rif_file);
	}
	#endif


	return TRUE;

}


BOOL Lockable_Chunk_With_Children::update_chunk_in_file(HANDLE &rif_file)
{

	unsigned long bytes_read;
	int length = 0;

	const char * hd_id = get_head_id();

	if (!hd_id) return FALSE;

	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	//twprintf("\nLooking for chunks in file\n");

	List<int> shpfptrs;
	list_chunks_in_file(& shpfptrs, rif_file, identifier);

	// look through chunks for the save of our current chunk

	//twprintf("Checking each chunk\n");

	LIF<int> sfpl(&shpfptrs);

	if (shpfptrs.size()) {
		for (; !sfpl.done(); sfpl.next()) {

			SetFilePointer (rif_file, sfpl()+8,0,FILE_BEGIN);

			ReadFile (rif_file, (long *) &(length), 4, &bytes_read, 0);

			SetFilePointer (rif_file, sfpl(),0,FILE_BEGIN);
			if (file_equals(rif_file)) break;
		}
	}

	// then load the file after that chunk into a buffer,
	// output the chunk and write the buffer
	// unless the chunk is the last one in the file

	//twprintf("Updating file\n");

	if (!sfpl.done())
	{

		int file_length = GetFileSize(rif_file,0);

		if (file_length > (sfpl() + length)) {
							
			SetFilePointer (rif_file, sfpl() + length,0,FILE_BEGIN);

			char * tempbuffer;
			tempbuffer = new char [file_length - (sfpl() + length)];

			ReadFile (rif_file, (long *) tempbuffer, (file_length - (sfpl() + length)), &bytes_read, 0);

			SetFilePointer (rif_file, sfpl() ,0,FILE_BEGIN);
			
			if (!deleted)
			{
				size_chunk();
				output_chunk(rif_file);
			}
			
			WriteFile (rif_file, (long *) tempbuffer, (file_length - (sfpl() + length)), &bytes_read, 0);

			delete [] tempbuffer;

			SetEndOfFile (rif_file);
		}
		else{

			SetFilePointer (rif_file, sfpl() ,0,FILE_BEGIN);
			if (!deleted)
			{
				size_chunk();
				output_chunk(rif_file);
			}
			SetEndOfFile (rif_file);
		}

	}
	else {

		SetFilePointer (rif_file,0 ,0,FILE_END);
		if (!deleted)
		{
			size_chunk();
			output_chunk(rif_file);
		}
		SetEndOfFile (rif_file);
	}

	local_lock = FALSE;

	updated = FALSE;
	
	int file_length = GetFileSize(rif_file,0);
	SetFilePointer (rif_file,8,0,FILE_BEGIN);

	WriteFile (rif_file, (long *) &file_length, 4, &bytes_read, 0);

	
	// DO NOT PUT ANY CODE AFTER THIS
	
	if (deleted) delete this;
	
	// OR ELSE !!!
	
	return TRUE;

}


size_t Lockable_Chunk_With_Children::size_chunk_for_process()
{
	if (output_chunk_for_process)
		return size_chunk();
	return(chunk_size = 0);
}


void Lockable_Chunk_With_Children::fill_data_block_for_process(char * data_start)
{
	if (output_chunk_for_process)
	{
		fill_data_block(data_start);
		output_chunk_for_process = FALSE;
	}
}




///////////////////////////////////////

// Class File_Chunk functions

/*
Children for File_Chunk :
"REBSHAPE"		Shape_Chunk
"RSPRITES"		AllSprites_Chunk
"RBOBJECT"		Object_Chunk
"RIFVERIN"		RIF_Version_Num_Chunk
"REBENVDT"		Environment_Data_Chunk
"REBENUMS"		Enum_Chunk
"OBJCHIER"		Object_Hierarchy_Chunk
"OBHALTSH"		Object_Hierarchy_Alternate_Shape_Set_Chunk
"HIDEGDIS"		Hierarchy_Degradation_Distance_Chunk
"INDSOUND"		Indexed_Sound_Chunk
"HSETCOLL"		Hierarchy_Shape_Set_Collection_Chunk
"DUMMYOBJ"		Dummy_Object_Chunk

*/

File_Chunk::File_Chunk(const char * file_name)
: Chunk_With_Children (NULL, "REBINFF2")
{
// Load in whole chunk and traverse
	char rifIsCompressed = FALSE;
	char *uncompressedData = NULL;
	HANDLE rif_file;
	DWORD file_size;
	DWORD file_size_from_file;
	unsigned long bytes_read;
	char * buffer;
	char * buffer_ptr;
	char id_buffer[9];

	Parent_File = this;

	error_code = 0;
	object_array_size=0;
	object_array=0;

	filename = new char [strlen(file_name) + 1];

	strcpy (filename, file_name);

	rif_file = CreateFileA (file_name, GENERIC_READ, 0, 0, OPEN_EXISTING, 
					FILE_FLAG_RANDOM_ACCESS, 0);

	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHUNK_FAILED_ON_LOAD;
		return;
	}

	file_size = GetFileSize (rif_file, NULL);	

	
	if (!ReadFile(rif_file, id_buffer, 8, &bytes_read, 0)) {
		error_code = CHUNK_FAILED_ON_LOAD;
		CloseHandle (rif_file);
		return;
	}	

	#if UseOldChunkLoader
	if (strncmp (id_buffer, "REBINFLF", 8)) {
		error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
		CloseHandle (rif_file);
		return;
	}	
	#else
	/* KJL 16:46:14 19/09/98 - check for a compressed rif */
	if (!strncmp (id_buffer, COMPRESSED_RIF_IDENTIFIER, 8))
	{
		rifIsCompressed = TRUE;
	}	
	else if (strncmp (id_buffer, "REBINFF2", 8))
	{
		error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
		CloseHandle (rif_file);
		return;
	}	
	#endif
	buffer = new char [file_size];

	/* KJL 17:57:44 19/09/98 - if the rif is compressed, we must load the whole
	file in and then pass it to the decompression routine, which will return a
	pointer to the original data. */
	if (rifIsCompressed)
	{
		if (!ReadFile(rif_file, buffer+8, (file_size-8), &bytes_read, 0))
		{
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			return;
		}	
		uncompressedData = HuffmanDecompress((HuffmanPackage*)buffer); 		
		file_size = ((HuffmanPackage*)buffer)->UncompressedDataSize;
		
		delete [] buffer; // kill the buffer the compressed file was loaded into

		buffer_ptr = buffer = uncompressedData+12; // skip header data
	}
	else // the normal uncompressed approach:
	{
		if (!ReadFile(rif_file, &file_size_from_file, 4, &bytes_read, 0)) {
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			return;
		}	

		if (file_size != file_size_from_file) {
			error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
			CloseHandle (rif_file);
			return;
		}	

		if (!ReadFile(rif_file, buffer, (file_size-12), &bytes_read, 0))
		{
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			return;
		}
		buffer_ptr = buffer;
	}

	// Process the RIF
	// The start of the first chunk

	while ((buffer_ptr-buffer)< ((signed) file_size-12) && !error_code) {

		if ((*(int *)(buffer_ptr + 8)) + (buffer_ptr-buffer) > ((signed)file_size-12)) {
			error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
			break;
		}

		DynCreate(buffer_ptr);
		buffer_ptr += *(int *)(buffer_ptr + 8);

	}

	/* KJL 17:59:42 19/09/98 - release the memory holding the rif */
	if (rifIsCompressed)
	{
		free(uncompressedData);
	}
	else
	{
		delete [] buffer;
	}

	CloseHandle (rif_file);

	post_input_processing();

}

File_Chunk::File_Chunk()
: Chunk_With_Children (NULL, "REBINFF2")
{
// Empty File chunk
	new RIF_Version_Num_Chunk (this);
	filename = 0;

	object_array_size=0;
	object_array=0;
}


File_Chunk::~File_Chunk()
{
	if (filename)
		delete [] filename;

	if(object_array)
		free(object_array);
}

#define SAVE_USING_COMPRESSION 1

BOOL File_Chunk::write_file (const char * fname)
{
	if(!fname) return FALSE;
	//if a read_only file exists with this filename , then abort attempt to save
	DWORD attributes = GetFileAttributesA(fname);
	if (0xffffffff!=attributes)
	{
		if (attributes & FILE_ATTRIBUTE_READONLY)
		{
			return FALSE;
		}
	}
	
	
	
	HANDLE rif_file;

	if (filename) delete [] filename;

	filename = new char [strlen(fname) + 1];
	strcpy (filename, fname);
		
	//save under a temporary name in case a crash occurs during save;
	int filename_start_pos=0;
	int pos=0;
	while(fname[pos])
	{
		if(fname[pos]=='\\' || fname[pos]==':')
		{
			filename_start_pos=pos+1;
		}
		//go to next MBCS character in string
		pos+=_mbclen((unsigned const char*)&fname[pos]);
	}
	if(!fname[filename_start_pos]) return FALSE;
	
	char* temp_name=new char[strlen(fname)+7];
	strcpy(temp_name,fname);
	strcpy(&temp_name[filename_start_pos],"~temp~");
	strcpy(&temp_name[filename_start_pos+6],&fname[filename_start_pos]);

	prepare_for_output();

	
	#if SAVE_USING_COMPRESSION
	//create a block containing the uncompressed rif file
	unsigned char* uncompressedData = (unsigned char*) make_data_block_from_chunk();
	if(!uncompressedData) return FALSE;

	//do the compression thing
	HuffmanPackage *outPackage = HuffmanCompression(uncompressedData,chunk_size);
	delete [] uncompressedData;
	if(!outPackage) return FALSE;

	//and now try to write the file
	rif_file = CreateFileA (temp_name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 
					FILE_FLAG_RANDOM_ACCESS, 0);

	if (rif_file == INVALID_HANDLE_VALUE) {
		delete [] temp_name;
		free (outPackage);
		return FALSE;
	}
		
	unsigned long junk;
	BOOL ok;

	ok = WriteFile (rif_file, (long *) outPackage,outPackage->CompressedDataSize+sizeof(HuffmanPackage), &junk, 0);

	CloseHandle (rif_file);

	free (outPackage);

	if(!ok) return FALSE;
	
	
	#else
	size_chunk();
	
	rif_file = CreateFileA (temp_name, GENERIC_WRITE, 0, 0, CREATE_ALWAYS, 
					FILE_FLAG_RANDOM_ACCESS, 0);

	if (rif_file == INVALID_HANDLE_VALUE) {
		delete [] temp_name;
		return FALSE;
	}

	if (!(this->output_chunk(rif_file)))
	{
		CloseHandle (rif_file);
		delete [] temp_name;
		return FALSE;
	}

	CloseHandle (rif_file);
	#endif

	//Delete the old file with this name (if it exists) , and rename the temprary file
	DeleteFileA(fname);
	MoveFileA(temp_name,fname);

	delete [] temp_name;

	return TRUE;
}	

// the file_chunk must link all of its shapes & objects together

void File_Chunk::post_input_processing()
{
	List<Shape_Chunk *> shplist;
	List<Object_Chunk *> objlist;

	List<Chunk *> child_lists;

	lookup_child("REBSHAPE",child_lists);

	while (child_lists.size()) {
		shplist.add_entry((Shape_Chunk *)child_lists.first_entry());
		child_lists.delete_first_entry();
	}

	lookup_child("RBOBJECT",child_lists);

	while (child_lists.size()) {
		objlist.add_entry((Object_Chunk *)child_lists.first_entry());
		child_lists.delete_first_entry();
	}

	for (LIF<Shape_Chunk *> sli(&shplist); !sli.done(); sli.next())
	{
		Shape_Chunk::max_id = max (Shape_Chunk::max_id,sli()->get_header()->file_id_num);	
	}
	Shape_Chunk** shape_array=new Shape_Chunk*[Shape_Chunk::max_id+1];

	for(sli.restart();!sli.done();sli.next())
	{
		shape_array[sli()->get_header()->file_id_num]=sli();
	}
	
	for (LIF<Object_Chunk *> ol(&objlist); !ol.done(); ol.next())
	{
		ol()->assoc_with_shape(shape_array[ol()->get_header()->shape_id_no]);
	}

	delete shape_array;
	

	Chunk_With_Children::post_input_processing();	
}



BOOL File_Chunk::check_file()
{
	if (!filename) return TRUE;

	#if DisableLock
	return(TRUE);
	#else
	
	int flags;
	int v_no;
	char locker[17];


	HANDLE rif_file;
	unsigned long bytes_read;

	int tries = 0;

	rif_file = CreateFileA (filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 
						FILE_FLAG_RANDOM_ACCESS, 0);

	while(rif_file == INVALID_HANDLE_VALUE && tries<10)
	{
		rif_file = CreateFileA (filename, GENERIC_READ, 0, 0, OPEN_EXISTING, 
						FILE_FLAG_RANDOM_ACCESS, 0);
	 	tries ++;
		clock_t ctime = clock();
		double secs	= (double)ctime / (double)CLOCKS_PER_SEC;
		double secsgone;
		do
		{
			ctime = clock();
			secsgone = (double)ctime / (double)CLOCKS_PER_SEC;
		}
		while( (secsgone-secs)<1);
	}	

	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHECK_FAILED_NOT_OPEN;
		return FALSE;
	}


	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHECK_FAILED_NOT_OPEN;
		return FALSE;
	}

	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	List<int> obfptrs;
	list_chunks_in_file(& obfptrs, rif_file, "RBOBJECT");

// ok, go through the objects, first locate the header and find
// the associated object.

	if (obfptrs.size()) {

		for (LIF<int> obflst(&obfptrs); !obflst.done(); obflst.next()) {
			
			// go to start of chunk
			SetFilePointer (rif_file,obflst(),0,FILE_BEGIN);
			
			// get header list
			List<int> obhead;
			list_chunks_in_file(& obhead, rif_file, "OBJHEAD1");

			assert (obhead.size() == 1);

			// go to lock status in header
			SetFilePointer(rif_file,obhead.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			ReadFile (rif_file, (long *) locker, 16, &bytes_read, 0);

			// go to version number
			SetFilePointer(rif_file,obhead.first_entry() + 88,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &v_no, 4, &bytes_read, 0);

			char name[50];

			// get object identifier
			SetFilePointer(rif_file,obhead.first_entry() + 96,0,FILE_BEGIN);
			int i = 0; 
			do ReadFile (rif_file, (long *) (name + i), 1, &bytes_read, 0);
			while (name[i++] != 0);

			Object_Chunk * tmpob;
			Object_Header_Chunk * tmpobh;
			List<Chunk *> obchs;
			lookup_child ("RBOBJECT",obchs);

			if (obchs.size()){
				for (LIF<Chunk *> obchls(&obchs); !obchls.done(); obchls.next()){
					tmpob = (Object_Chunk *)obchls();
					// if this is the same object
					if (!strcmp (name, tmpob->object_data.o_name)) break;
				}
				if (!obchls.done()) {
					tmpobh = tmpob->get_header();
					if (tmpobh) {
						if (tmpobh->version_no < v_no)
							tmpob->updated_outside = TRUE;
						if (flags & GENERAL_FLAG_LOCKED) {
							// do the lock check
							if (!tmpob->local_lock) {
								tmpob->external_lock = TRUE;
								strncpy(tmpobh->lock_user, locker,16);
								tmpobh->lock_user[16] = '\0';
							}
							else if (strncmp(tmpobh->lock_user, locker,16)) {
								tmpob->external_lock = TRUE;
								strncpy(tmpobh->lock_user, locker,16);
								tmpobh->lock_user[16] = '\0';
							}
						}
					}

				}
				
			}

		}

	}

	// shapes
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	List<int> shpfptrs;
	list_chunks_in_file(& shpfptrs, rif_file, "REBSHAPE");

	if (shpfptrs.size()) {

		for (LIF<int> shpflst(&shpfptrs); !shpflst.done(); shpflst.next()) {
			
			// go to start of chunk
			SetFilePointer (rif_file,shpflst(),0,FILE_BEGIN);
			
			// get header list
			List<int> shphead;
			list_chunks_in_file(& shphead, rif_file, "SHPHEAD1");

			assert (shphead.size() == 1);

			// go to lock status in header
			SetFilePointer(rif_file,shphead.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			ReadFile (rif_file, (long *) locker, 16, &bytes_read, 0);

			int id;			
			// get shape identifier
			ReadFile (rif_file, (long *) &id, 4, &bytes_read, 0);


			// Here we update max_id
			Shape_Chunk::max_id = max (Shape_Chunk::max_id,id);	

			// go to version number
			SetFilePointer(rif_file,shphead.first_entry() + 100,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &v_no, 4, &bytes_read, 0);

			Shape_Chunk * tmpshp;
			Shape_Header_Chunk * tmpshph;
			List<Chunk *> shpchs;
			lookup_child ("REBSHAPE",shpchs);

			if (shpchs.size()){
				for (LIF<Chunk *> shpchls(&shpchs); !shpchls.done(); shpchls.next()){
					tmpshp = (Shape_Chunk *)shpchls();
					tmpshph = tmpshp->get_header();
					// if this is the same object
					if (tmpshph) 
						if (id == tmpshph->file_id_num) break;
				}
				if (!shpchls.done()) {
					if (tmpshph->version_no < v_no)
						tmpshp->updated_outside = TRUE;
					if (flags & GENERAL_FLAG_LOCKED) {
						// do the lock check
						if (!tmpshp->local_lock) {
							tmpshp->external_lock = TRUE;
							strncpy(tmpshph->lock_user, locker,16);
							tmpshph->lock_user[16] = '\0';
						}
						else if (strncmp(tmpshph->lock_user, locker,16)) {
							tmpshp->external_lock = TRUE;
							strncpy(tmpshph->lock_user, locker,16);
							tmpshph->lock_user[16] = '\0';
						}
					}
					
				}
				
			}

		}

	}

	// sprites
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	List<int> sprfptrs;
	list_chunks_in_file(& sprfptrs, rif_file, "RSPRITES");
	List<Chunk *> sprchlst;
	lookup_child("RSPRITES",sprchlst);

	AllSprites_Chunk * sprch;
	AllSprites_Header_Chunk * sprhead = 0;

	if (sprchlst.size())
	{
		sprch = (AllSprites_Chunk *)sprchlst.first_entry();
		sprhead = sprch->get_header();
	}

	if (sprhead)
	{
		if (sprfptrs.size())
		{
			SetFilePointer (rif_file,sprfptrs.first_entry(),0,FILE_BEGIN);

			// get header list
			List<int> sprchhl;
			list_chunks_in_file(& sprchhl, rif_file, "ASPRHEAD");

			assert (sprchhl.size() == 1);

			// go to lock status in header
			SetFilePointer(rif_file,sprchhl.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			ReadFile (rif_file, (long *) locker, 16, &bytes_read, 0);

			// go to version number
			SetFilePointer(rif_file,sprchhl.first_entry() + 32,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &v_no, 4, &bytes_read, 0);

			if (sprhead->version_no < v_no)
			{
				sprch->updated_outside = TRUE;
			}
			if (flags & GENERAL_FLAG_LOCKED) 
			{
				if (!sprch->local_lock) {
					sprch->external_lock = TRUE;
					strncpy(sprhead->lock_user, locker,16);
					sprhead->lock_user[16] = '\0';
				}
				else if (strncmp(sprhead->lock_user, locker,16)) {
					sprch->external_lock = TRUE;
					strncpy(sprhead->lock_user, locker,16);
					sprhead->lock_user[16] = '\0';
				}
			}
		}
	}
	
	// environment data
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	List<int> edfptrs;
	list_chunks_in_file(& edfptrs, rif_file, "REBENVDT");

	Environment_Data_Chunk * ed;
	Environment_Data_Header_Chunk * edhead = 0;

	
	ed = (Environment_Data_Chunk *)lookup_single_child("REBENVDT");
	if (ed)
	{
		edhead = ed->get_header();
	}

	if (edhead)
	{
		if (edfptrs.size())
		{
			SetFilePointer (rif_file,edfptrs.first_entry(),0,FILE_BEGIN);

			// get header list
			List<int> edhl; list_chunks_in_file(& edhl, rif_file, "ENDTHEAD");

			assert (edhl.size() == 1);

			// go to lock status in header
			SetFilePointer(rif_file,edhl.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			ReadFile (rif_file, (long *) locker, 16, &bytes_read, 0);

			// go to version number
			SetFilePointer(rif_file,edhl.first_entry() + 32,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &v_no, 4, &bytes_read, 0);

			if (edhead->version_no < v_no)
			{
				ed->updated_outside = TRUE;
			}
			if (flags & GENERAL_FLAG_LOCKED) 
			{
				if (!ed->local_lock) {
					ed->external_lock = TRUE;
					strncpy(edhead->lock_user, locker,16);
					edhead->lock_user[16] = '\0';
				}
				else if (strncmp(edhead->lock_user, locker,16)) {
					ed->external_lock = TRUE;
					strncpy(edhead->lock_user, locker,16);
					edhead->lock_user[16] = '\0';
				}
			}
		}
	}

	// check file for REBENUMS chunk

	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	List<int> enumfptrs; list_chunks_in_file(& enumfptrs, rif_file, "REBENUMS");

	Enum_Chunk * enumch;
	Enum_Header_Chunk * enumhead = 0;

	enumch = (Enum_Chunk *)lookup_single_child("REBENUMS");
	if (enumch)
	{
		enumhead = enumch->get_header();
	}

	if (enumhead)
	{
		if (enumfptrs.size())
		{
			SetFilePointer (rif_file,enumfptrs.first_entry(),0,FILE_BEGIN);

			// get header list
			List<int> enumchhl; list_chunks_in_file(& enumchhl, rif_file, "ENUMHEAD");

			assert (enumchhl.size() == 1);

			// go to lock status in header
			SetFilePointer(rif_file,enumchhl.first_entry() + 12,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &flags, 4, &bytes_read, 0);
			ReadFile (rif_file, (long *) locker, 16, &bytes_read, 0);

			// go to version number
			SetFilePointer(rif_file,enumchhl.first_entry() + 32,0,FILE_BEGIN);
			ReadFile (rif_file, (long *) &v_no, 4, &bytes_read, 0);

			if (enumhead->version_no < v_no)
			{
				enumch->updated_outside = TRUE;
			}
			if (flags & GENERAL_FLAG_LOCKED) 
			{
				if (!enumch->local_lock) {
					enumch->external_lock = TRUE;
					strncpy(enumhead->lock_user, locker,16);
					enumhead->lock_user[16] = '\0';
				}
				else if (strncmp(enumhead->lock_user, locker,16)) {
					enumch->external_lock = TRUE;
					strncpy(enumhead->lock_user, locker,16);
					enumhead->lock_user[16] = '\0';
				}
			}
		}
	}

	CloseHandle (rif_file);

	return TRUE;
	#endif
}


#if InterfaceEngine

	extern File_Chunk * Env_Chunk;
	
#endif
	
BOOL File_Chunk::update_file()
{

	#if DisableLock
	if (!filename) return FALSE;

	char tempname [256];
	strcpy (tempname, filename);

	List<Shape_Chunk *> slist;
	list_shapes(&slist);
	List<Object_Chunk *> olist;
	list_objects(&olist);
	
	for (LIF<Shape_Chunk *> sli(&slist); !sli.done(); sli.next())
	{
		if (sli()->deleted)
		{
			delete sli();
		}
	}
	
	for (LIF<Object_Chunk *> oli(&olist); !oli.done(); oli.next())
	{
		if (oli()->deleted)
		{
			delete oli();
		}
	}
	
	
	return(write_file(tempname));
	
	#else

#if InterfaceEngine

	// log, to track error
	char fname [256];
	char * dotpos;

	strcpy (fname, filename);

	dotpos = strrchr (fname, '.');
	
	sprintf (dotpos, ".log");
	
	FILE * log = fopen (fname, "a");

#endif

	if (!filename) return FALSE;

	twprintf("Updating %s\n",filename);

	check_file();

	HANDLE rif_file;
	unsigned long bytes_read;

	int tries = 0;

	//twprintf("Opening file\n");
	
	rif_file = CreateFileA (filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
				FILE_FLAG_RANDOM_ACCESS, 0);

	if (rif_file == INVALID_HANDLE_VALUE)
	{
		DWORD error_num = GetLastError();
		switch (error_num)
		{
			case ERROR_SHARING_VIOLATION:
				twprintf("Sharing violation - retrying\n");
				break;
			case ERROR_ACCESS_DENIED:
				twprintf("File is Read Only\n");
				return FALSE;
			default:
				twprintf("Unknown error updating file, Error code %#08x\n",error_num);
				return FALSE;
		}
	}
				
	while(rif_file == INVALID_HANDLE_VALUE && tries<10)
	{
		twprintf("Again...\n");
	 	rif_file = CreateFileA (filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
	 				FILE_FLAG_RANDOM_ACCESS, 0);
	 	tries ++;
		clock_t ctime = clock();
		double secs	= (double)ctime / (double)CLOCKS_PER_SEC;
		double secsgone;
		do
		{
			ctime = clock();
			secsgone = (double)ctime / (double)CLOCKS_PER_SEC;
		}
		while( (secsgone-secs)<1);
	}	

	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHECK_FAILED_NOT_OPEN;
		twprintf("ERROR - SHARING VIOLATION UNRESOLVED\n\n");
		return FALSE;
	}

	//twprintf("Opened\n\n");
	
	prepare_for_output();
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);

	//twprintf("Version info\n");
	
	List<int> verinf; list_chunks_in_file(& verinf, rif_file, "RIFVERIN");

// taking first entry of this list, if there are more - tough ha ha
// there shouldn't be

// Just increment it by one and reoutput
// check_file sets the internal copy of the chunk to the current file
// setting, so we need to increment that as well
	
	if (verinf.size()) {

		int f_version_num;
		SetFilePointer (rif_file,verinf.first_entry() + 12,0,FILE_BEGIN);
		ReadFile (rif_file, (long *) &f_version_num, 4, &bytes_read, 0);

		SetFilePointer (rif_file,verinf.first_entry() + 12,0,FILE_BEGIN);

		RIF_Version_Num_Chunk* rvnc=(RIF_Version_Num_Chunk*)lookup_single_child ("RIFVERIN");
		
		if (rvnc)
			rvnc->file_version_no++;
		
		f_version_num++;

		WriteFile (rif_file, (long *) &f_version_num, 4, &bytes_read, 0);

	}

//	go through the list of shape chunks looking for shape chunks to output
	//twprintf("\nShapes\n");
		
	List<Chunk *> shplst;
	lookup_child ("REBSHAPE",shplst);

	if (shplst.size())
		for (LIF<Chunk *> sli(&shplst); !sli.done(); sli.next()) {

			Shape_Chunk * tmpshpptr = ((Shape_Chunk *)sli());

			if (tmpshpptr->updated && 
				!(tmpshpptr->updated_outside || tmpshpptr->external_lock)) 
				tmpshpptr->update_chunk_in_file(rif_file);
		}


//	go through the list of object chunks looking for chunks to output
	//twprintf("\nObjects\n");
		
	List<Chunk *> oblst;
	lookup_child ("RBOBJECT",oblst);

	if (oblst.size()) 
		for (LIF<Chunk *> oli(&oblst); !oli.done(); oli.next()) {
			
			Object_Chunk * tmpobptr = ((Object_Chunk *)oli());
			
			if (tmpobptr->updated && !(tmpobptr->updated_outside || tmpobptr->external_lock)) 
				tmpobptr->update_chunk_in_file(rif_file);

		}


	//twprintf("\nSprites\n");

	List<Chunk *> sprfptrs;
	lookup_child ("RSPRITES",sprfptrs);
	AllSprites_Chunk * sprch;

	if (sprfptrs.size())
	{
		sprch = (AllSprites_Chunk *)sprfptrs.first_entry();
		if (sprch->updated && 
			!(sprch->updated_outside || sprch->external_lock)) 
			sprch->update_chunk_in_file(rif_file);
	}

	//twprintf("\nEnvironment data\n");

	Environment_Data_Chunk * ed;

	ed = (Environment_Data_Chunk *)lookup_single_child ("REBENVDT");
	if (ed)
	{

#if InterfaceEngine

	fprintf (log, "Env_Data %d %d %d %d", ed->updated, ed->local_lock, ed->updated_outside, ed->external_lock);

#endif

		if (ed->updated && 
			!(ed->updated_outside || ed->external_lock)) 
			ed->update_chunk_in_file(rif_file);
	}

	//twprintf("\nEnum data\n");

	List<Chunk *> enumfptrs;
	lookup_child ("REBENUMS",enumfptrs);
	Enum_Chunk * enumch;

	if (enumfptrs.size())
	{
		enumch = (Enum_Chunk *)enumfptrs.first_entry();
		if (enumch->updated && 
			!(enumch->updated_outside || enumch->external_lock)) 
			enumch->update_chunk_in_file(rif_file);
	}

	//

	int file_length = GetFileSize(rif_file,0);
	SetFilePointer (rif_file,8,0,FILE_BEGIN);

	WriteFile (rif_file, (long *) &file_length, 4, &bytes_read, 0);

	CloseHandle (rif_file);

#if InterfaceEngine

	fclose (log);

#endif
	return TRUE;

	#endif //DisableLock
}

BOOL File_Chunk::update_chunks_from_file()
{
	#if DisableLock
	return(TRUE);
	#endif
	
	if (!filename) return FALSE;
	check_file();
	
	HANDLE rif_file;
	unsigned long bytes_read;
	
	rif_file = CreateFileA (filename, GENERIC_WRITE + GENERIC_READ, 0, 0, OPEN_EXISTING, 
					FILE_FLAG_RANDOM_ACCESS, 0);

	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHECK_FAILED_NOT_OPEN;
		return FALSE;
	}
	
	SetFilePointer (rif_file,0,0,FILE_BEGIN);
	
	List<int> verinf; list_chunks_in_file(& verinf, rif_file, "RIFVERIN");

	if (verinf.size()) {

		int f_version_num;
		SetFilePointer (rif_file,verinf.first_entry() + 12,0,FILE_BEGIN);
		ReadFile (rif_file, (long *) &f_version_num, 4, &bytes_read, 0);

		SetFilePointer (rif_file,verinf.first_entry() + 12,0,FILE_BEGIN);

		List<Chunk *> lverinf;
		lookup_child ("RIFVERIN",lverinf);

		if (lverinf.size())
			if (f_version_num == ((RIF_Version_Num_Chunk *)(lverinf.first_entry()))->file_version_no){
				CloseHandle (rif_file);
				return TRUE;
			}

	}

	//	go through the list of object chunks looking for chunks to input
		
	List<Chunk *> oblst;
	lookup_child ("RBOBJECT",oblst);

	if (oblst.size()) 
		for (LIF<Chunk *> oli(&oblst); !oli.done(); oli.next()) {
			
			Object_Chunk * tmpobptr = ((Object_Chunk *)oli());
			
			if (tmpobptr->updated_outside) {
			// find the chunk, then input it to a buffer and create a new object
			// from the buffer
				SetFilePointer (rif_file,0,0,FILE_BEGIN);
				
				List<int> obfptrs; list_chunks_in_file(& obfptrs, rif_file, "RBOBJECT");

				char name[50];

				LIF<int> ofpl(&obfptrs);

				if (obfptrs.size()) {
					for (; !ofpl.done(); ofpl.next()) {

						SetFilePointer (rif_file, ofpl(),0,FILE_BEGIN);
						// get header list
						List<int> obhead; list_chunks_in_file(& obhead, rif_file, "OBJHEAD1");

						assert (obhead.size() == 1);

						// get object identifier
						SetFilePointer(rif_file,obhead.first_entry() + 96,0,FILE_BEGIN);
						int i = 0; 
						do ReadFile (rif_file, (long *) (name + i), 1, &bytes_read, 0);
						while (name[i++] != 0);

						if (!strcmp(name, tmpobptr->object_data.o_name)) break;
					}
				}

				if (!ofpl.done()) {
	
					char * buffer;

					SetFilePointer (rif_file,ofpl()+8,0,FILE_BEGIN);
					int length;
					ReadFile(rif_file, (long *) &length, 4, &bytes_read, 0);
					buffer = new char [length];
					ReadFile(rif_file, (long *) buffer, length-12, &bytes_read, 0);
					new Object_Chunk (this, buffer, length-12);
					delete [] buffer;
					if (tmpobptr->get_header())
						if (tmpobptr->get_header()->associated_shape)
							tmpobptr->deassoc_with_shape(tmpobptr->get_header()->associated_shape);
					delete tmpobptr;

				}


			}

		}

//	go through the list of shape chunks looking for shape chunks to input
		
	List<Chunk *> shplst;
	lookup_child ("REBSHAPE",shplst);

	if (shplst.size())
		for (LIF<Chunk *> sli(&shplst); !sli.done(); sli.next()) {

			Shape_Chunk * tmpshpptr = ((Shape_Chunk *)sli());

			Shape_Header_Chunk * shhead = tmpshpptr->get_header();

			if (!shhead) continue;
			
			if (tmpshpptr->updated_outside) {
			// find the chunk, then input it to a buffer and create a new object
			// from the buffer
				SetFilePointer (rif_file,0,0,FILE_BEGIN);

				List<int> shfptrs; list_chunks_in_file(& shfptrs, rif_file, "REBSHAPE");

				LIF<int> sfpl(&shfptrs);

				if (shfptrs.size()) {
					for (sfpl.restart(); !sfpl.done(); sfpl.next()) {

						SetFilePointer (rif_file, sfpl(),0,FILE_BEGIN);
						// get header list
						List<int> shphead; list_chunks_in_file(& shphead, rif_file, "SHPHEAD1");

						assert (shphead.size() == 1);

						// get object identifier
						SetFilePointer(rif_file,shphead.first_entry() + 32,0,FILE_BEGIN);
						int sh_number; 
						ReadFile (rif_file, (long *) &sh_number, 4, &bytes_read, 0);

						if (sh_number == shhead->file_id_num) break;
					}
				}

				if (!sfpl.done()) {
	
					char * buffer;

					SetFilePointer (rif_file,sfpl()+8,0,FILE_BEGIN);
					int length;
					ReadFile(rif_file, (long *) &length, 4, &bytes_read, 0);
					buffer = new char [length];
					ReadFile(rif_file, (long *) buffer, length-12, &bytes_read, 0);
					new Shape_Chunk (this, buffer, length-12);
					delete [] buffer;
					// Associate with the new objects
					if ((shhead->associated_objects_store).size())
					{
						for (LIF<Object_Chunk *> aol(&(shhead->associated_objects_store)); !aol.done(); aol.next())
						{
							tmpshpptr->deassoc_with_object(aol());
						}
					}
					delete tmpshpptr;
				}
			}
		}

	post_input_processing();

	CloseHandle(rif_file);

	return TRUE;

}

void File_Chunk::list_objects(List<Object_Chunk *> * pList)
{
	Chunk * child_ptr = children;
	
	while (pList->size())
		pList->delete_first_entry();

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("RBOBJECT", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				pList->add_entry((Object_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

}

void File_Chunk::list_shapes(List<Shape_Chunk *> * pList)
{
	Chunk * child_ptr = children;

	while (pList->size())
		pList->delete_first_entry();

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("REBSHAPE", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				pList->add_entry((Shape_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

}

void File_Chunk::list_dummy_objects(List<Dummy_Object_Chunk *> * pList){
	Chunk * child_ptr = children;
	
	while (pList->size())
		pList->delete_first_entry();

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("DUMMYOBJ", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				pList->add_entry((Dummy_Object_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

}

Environment_Data_Chunk * File_Chunk::get_env_data()
{
	List<Environment_Data_Chunk *> e_list;
	Chunk * child_ptr = children;

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("REBENVDT", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				e_list.add_entry((Environment_Data_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

	// There can be only ONE.
	assert (e_list.size() < 2);
	
	if (e_list.size())
		return e_list.first_entry();
	else
	{
		return(0);
	}	
}

void File_Chunk::build_object_array()
{
	List<Object_Chunk*> oblist;
	list_objects(&oblist);

	if(object_array)
	{
		free(object_array);
		object_array=0;
	}	
	object_array_size=0;

	LIF<Object_Chunk*> oblif(&oblist);

	//find the highest object index
	for(oblif.restart();!oblif.done();oblif.next())
	{
		object_array_size=max(object_array_size,oblif()->object_data.index_num+1);
	}

	if(object_array_size<=0) return;

	object_array = (Object_Chunk**) malloc(sizeof(Object_Chunk*)*object_array_size);
	for(int i=0;i<object_array_size;i++)
	{
		object_array[i]=0;
	}

	//now fill in object array

	for(oblif.restart();!oblif.done();oblif.next())
	{
		int index=oblif()->object_data.index_num;
		if(index>=0)
		{
			object_array[index]=oblif();
		}
	}
}

Object_Chunk* File_Chunk::get_object_by_index(int index)
{
	if(!object_array) build_object_array();
	if(index<0 || index>=object_array_size)return 0;
	return object_array[index];
}

void File_Chunk::assign_index_to_object(Object_Chunk* object)
{
	assert(object);

	if(!object_array) build_object_array();
	//see if there is a free index

	for(int i=0;i<object_array_size;i++)
	{
		if(!object_array[i])
		{
			object->object_data_store->index_num=i;
			object_array[i]=object;
			return;
		}
	}
	
	//add a new entry on the end of the array
	object_array_size++;
		
	object_array=(Object_Chunk**) realloc(object_array,sizeof(Object_Chunk*)*object_array_size);
	

	object->object_data_store->index_num=object_array_size-1;;
	object_array[object_array_size-1]=object;
}

/////////////////////////////////////////

// Class GodFather_Chunk functions

/*
Children for GodFather_Chunk :
"REBSHAPE"		Shape_Chunk
"RSPRITES"		AllSprites_Chunk
"RBOBJECT"		Object_Chunk
"RIFVERIN"		RIF_Version_Num_Chunk
"REBENVDT"		Environment_Data_Chunk
"REBENUMS"		Enum_Chunk
"OBJCHIER"		Object_Hierarchy_Chunk
"OBHALTSH"		Object_Hierarchy_Alternate_Shape_Set_Chunk

*/

GodFather_Chunk::GodFather_Chunk(char * buffer, size_t size)
: Chunk_With_Children (NULL, "REBINFF2")
{
	Parent_File = this;

	char * buffer_ptr = buffer;

	// The start of the first chunk

	while ((buffer_ptr-buffer)< ((signed)size-12) && !error_code) {

		if ((*(int *)(buffer_ptr + 8)) + (buffer_ptr-buffer) > ((signed)size-12)) {
			error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
			break;
		}

		DynCreate(buffer_ptr);
		buffer_ptr += *(int *)(buffer_ptr + 8);

	}

}

/////////////////////////////////////////

// Class RIF_Version_Num_Chunk functions

RIF_IMPLEMENT_DYNCREATE("RIFVERIN",RIF_Version_Num_Chunk)

void RIF_Version_Num_Chunk::fill_data_block(char * data_start)
{
	strncpy (data_start, identifier, 8);

	data_start += 8;

	*((int *) data_start) = chunk_size;

	data_start += 4;

	*((int *) data_start) = file_version_no;

}


/////////////////////////////////////////

// Class RIF_Name_Chunk functions
RIF_IMPLEMENT_DYNCREATE("RIFFNAME",RIF_Name_Chunk)

RIF_Name_Chunk::RIF_Name_Chunk (Chunk_With_Children * parent, const char * rname)
: Chunk (parent, "RIFFNAME")
{
	rif_name = new char [strlen(rname)+1];
	strcpy (rif_name, rname);
}

RIF_Name_Chunk::RIF_Name_Chunk (Chunk_With_Children * parent, const char * rdata, size_t /*rsize*/)
: Chunk (parent, "RIFFNAME")
{
	rif_name = new char [strlen(rdata)+1];
	strcpy (rif_name, rdata);
}

RIF_Name_Chunk::~RIF_Name_Chunk ()
{
	if (rif_name)
		delete [] rif_name;
}


void RIF_Name_Chunk::fill_data_block (char * data_start)
{
	strncpy (data_start, identifier, 8);

	data_start += 8;

	*((int *) data_start) = chunk_size;

	data_start += 4;
	
	strcpy (data_start, rif_name);

}


///////////////////////////////////////

/*
Children for RIF_File_Chunk :
"REBSHAPE"		Shape_Chunk
"RSPRITES"		AllSprites_Chunk
"RBOBJECT"		Object_Chunk
"RIFVERIN"		RIF_Version_Num_Chunk
"REBENVDT"		Environment_Data_Chunk
"OBJCHIER"		Object_Hierarchy_Chunk
"OBHALTSH"		Object_Hierarchy_Alternate_Shape_Set_Chunk

*/


RIF_File_Chunk::RIF_File_Chunk (Chunk_With_Children * parent, const char * file_name)
: Chunk_With_Children (parent, "SUBRIFFL")
{
	char rifIsCompressed = FALSE;
	char *uncompressedData = NULL;
	HANDLE rif_file;
	DWORD file_size;
	DWORD file_size_from_file;
	unsigned long bytes_read;
	char * buffer;
	char * buffer_ptr;
	char id_buffer[9];


	Chunk * ParentFileStore = Parent_File;
	
	Parent_File = this;
	
	error_code = 0;
	
	rif_file = CreateFileA (file_name, GENERIC_READ, 0, 0, OPEN_EXISTING, 
					FILE_FLAG_RANDOM_ACCESS, 0);


	if (rif_file == INVALID_HANDLE_VALUE) {
		error_code = CHUNK_FAILED_ON_LOAD;
		Parent_File = ParentFileStore;
		return;
	}

	file_size = GetFileSize (rif_file, NULL);	

	if (!ReadFile(rif_file, id_buffer, 8, &bytes_read, 0)) {
		error_code = CHUNK_FAILED_ON_LOAD;
		CloseHandle (rif_file);
		Parent_File = ParentFileStore;
		return;
	}	

	//check for compressed rif
	if (!strncmp (id_buffer, COMPRESSED_RIF_IDENTIFIER, 8))
	{
		rifIsCompressed = TRUE;
	}	
	else if (strncmp (id_buffer, "REBINFF2", 8)) {
		error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
		CloseHandle (rif_file);
		Parent_File = ParentFileStore;
		return;
	}	

	buffer = new char [file_size];
	
	/* KJL 17:57:44 19/09/98 - if the rif is compressed, we must load the whole
	file in and then pass it to the decompression routine, which will return a
	pointer to the original data. */
	if (rifIsCompressed)
	{
		if (!ReadFile(rif_file, buffer+8, (file_size-8), &bytes_read, 0))
		{
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			Parent_File = ParentFileStore;
			delete [] buffer;
			return;
		}	
		uncompressedData = HuffmanDecompress((HuffmanPackage*)buffer); 		
		file_size = ((HuffmanPackage*)buffer)->UncompressedDataSize;
		
		delete [] buffer; // kill the buffer the compressed file was loaded into

		buffer_ptr = buffer = uncompressedData+12; // skip header data
	}
	else // the normal uncompressed approach:
	{
		//get the file size stored in the rif file
		if (!ReadFile(rif_file, &file_size_from_file, 4, &bytes_read, 0)) {
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			Parent_File = ParentFileStore;
			delete [] buffer;
			return;
		}	

		//and compare with the actual file size
		if (file_size != file_size_from_file) {
			error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
			CloseHandle (rif_file);
			Parent_File = ParentFileStore;
			delete [] buffer;
			return;
		}	

		//read the rest of the file into the buffer
		if (!ReadFile(rif_file, buffer, (file_size-12), &bytes_read, 0))
		{
			error_code = CHUNK_FAILED_ON_LOAD;
			CloseHandle (rif_file);
			Parent_File = ParentFileStore;
			delete [] buffer;
			return;
		}
		buffer_ptr = buffer;
	}

	
	

	// Process the RIF

	// The start of the first chunk

	while ((buffer_ptr-buffer)< ((signed) file_size-12) && !error_code) {

		if ((*(int *)(buffer_ptr + 8)) + (buffer_ptr-buffer) > ((signed)file_size-12)) {
			error_code = CHUNK_FAILED_ON_LOAD_NOT_RECOGNISED;
			break;
		}

		DynCreate(buffer_ptr);
		buffer_ptr += *(int *)(buffer_ptr + 8);
	}

	/* KJL 17:59:42 19/09/98 - release the memory holding the rif */
	if (rifIsCompressed)
	{
		free(uncompressedData);
	}
	else
	{
		delete [] buffer;
	}

	CloseHandle (rif_file);

	post_input_processing();

	Parent_File = ParentFileStore;
	
}

void RIF_File_Chunk::post_input_processing()
{
	List<Shape_Chunk *> shplist;
	List<Object_Chunk *> objlist;

	List<Chunk *> child_lists;

	lookup_child("REBSHAPE",child_lists);

	while (child_lists.size()) {
		shplist.add_entry((Shape_Chunk *)child_lists.first_entry());
		child_lists.delete_first_entry();
	}

	lookup_child("RBOBJECT",child_lists);


	while (child_lists.size()) {
		objlist.add_entry((Object_Chunk *)child_lists.first_entry());
		child_lists.delete_first_entry();
	}

	for (LIF<Object_Chunk *> ol(&objlist); !ol.done(); ol.next()) {
		
		if (ol()->get_header()) {
			
			for (LIF<Shape_Chunk *> sl(&shplist); 
					 !sl.done(); sl.next()) {
				if (sl()->get_header())
					if (sl()->get_header()->file_id_num == ol()->get_header()->shape_id_no){
						ol()->assoc_with_shape(sl());
						break;
					}
			}
		}

	}	

	for (LIF<Shape_Chunk *> sli(&shplist); !sli.done(); sli.next())
	{
		Shape_Chunk::max_id = max (Shape_Chunk::max_id,sli()->get_header()->file_id_num);	
	}

	Chunk_With_Children::post_input_processing();	
}


void RIF_File_Chunk::list_objects(List<Object_Chunk *> * pList)
{
	Chunk * child_ptr = children;
	
	while (pList->size())
		pList->delete_first_entry();

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("RBOBJECT", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				pList->add_entry((Object_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}
}

void RIF_File_Chunk::list_shapes(List<Shape_Chunk *> * pList)
{
	Chunk * child_ptr = children;

	while (pList->size())
		pList->delete_first_entry();

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("REBSHAPE", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				pList->add_entry((Shape_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

}

Environment_Data_Chunk * RIF_File_Chunk::get_env_data()
{
	List<Environment_Data_Chunk *> e_list;
	Chunk * child_ptr = children;

	if (children)	
		while	(child_ptr != NULL) {
			if (strncmp ("REBENVDT", child_ptr->identifier, 8) == NULL)
			{
				assert (!child_ptr->r_u_miscellaneous());
				e_list.add_entry((Environment_Data_Chunk *)child_ptr);
			}
			child_ptr = child_ptr->next;
		}

	// There can be only ONE.
	assert (e_list.size() < 2);
	
	if (e_list.size())
		return e_list.first_entry();
	else
	{
		return(0);
	}	
}
