/****************************************************************************/
/*                                                                          */
/* Load/save routines                                                       */
/*                                                                          */
/****************************************************************************/

#include "standard.h"
#include "global.h"
#include "winmain.h"
#include "drawprimitive.h"
#include "texture.h"
#include "init.h"
#include "hwrender.h"
#include "file.h"
#include "ds.h"
#include "debug.h"
#include "picture.h"

#include "../game/cinema.h"
#include "../game/invfunc.h"
#include "../game/setup.h"
#include "../game/objects.h"
#include "../game/savegame.h"
#include "../game/camera.h"
#include "../game/effects.h"
#include "../game/box.h"
#include "../game/demo.h"
#include "../game/sound.h"

/********************************* GLOBAL VARIABLES *************************/

defglobal int GnTexturePages;

sint16 	*mesh_base, *floor_data;
char 	*room_text_base;
char 	*object_text_base;
char	*sprite_base;
char	cd_drive=0;

/*** locals ***/
local char LacLevelFileName[256];
local DWORD LdwPaletteOffset;
local DWORD LdwTextureOffset;
local DWORD LdwDepthQOffset;
local int LnTextureInfos;
local uint8 LabTextureUVFlag[MAX_TEXTURES];
local uint32 LdwReadFileTotal;

/*********************************** FUNCTION CODE **************************/

BOOL MyReadFile(HANDLE hFile,void* pBuffer,DWORD nNumberOfBytesToRead,DWORD* pNumberOfBytesRead,OVERLAPPED* pOverlapped)
	{
	LdwReadFileTotal+=nNumberOfBytesToRead;
	if (LdwReadFileTotal>16384)
		{
		LdwReadFileTotal=0;
		DD_SpinMessageLoop();
		}
	return ReadFile(hFile,pBuffer,nNumberOfBytesToRead,pNumberOfBytesRead,pOverlapped);
	}

/****************************************************************************************
 *!			Load all Texture pages into memory
 ***************************************************************************************/
int LoadTexturePages(HANDLE file)
{
	DWORD read;
	char *base;
	int i, num_pages;

	MyReadFile(file, &num_pages, sizeof(int), &read, NULL);

	if (App.nRenderMode==RENDERER_INTERNAL) //RF
		{
		for (i=0;i<num_pages;++i)
			{
			if (texture_page_ptrs[i]==0)
				texture_page_ptrs[i]=(char *)game_malloc(65536, TEXTURE_PAGES);
			MyReadFile(file, texture_page_ptrs[i], 65536 , &read, NULL);
			}
		SetFilePointer(file,num_pages*131072,0,FILE_CURRENT);	// skip 16bit tpages
		return 1;
		}


	bool t16BitTextures;
	
	if(DXD3DTexture(App.DeviceInfoPtr,App.DXConfigPtr).bPalette == false)
		t16BitTextures = true;
	else
		t16BitTextures = false;
	
	
	int nBytesPerPage=t16BitTextures?0x20000:0x10000;
	int nAllocAmount=num_pages*nBytesPerPage;
//	base=(char *)game_malloc(nAllocAmount,TEXTURE_PAGES);
	base=(char*)GlobalAlloc(GMEM_FIXED,nAllocAmount);
	if (base==0)
		return 0;
	if (t16BitTextures)
	{
		SetFilePointer(file,num_pages*65536,0,FILE_CURRENT);
		for (i=0;i<num_pages;++i)
			MyReadFile(file,base+(i*nBytesPerPage),nBytesPerPage,&read,NULL);
		HWR_LoadTexturePages(num_pages,base,0);
	}
	else
	{
		for (i=0;i<num_pages;++i)
			MyReadFile(file,base+(i*nBytesPerPage),nBytesPerPage,&read,NULL);
		SetFilePointer(file,num_pages*131072,0,FILE_CURRENT);
		HWR_LoadTexturePages(num_pages,base,game_palette);
	}
	GnTexturePages=num_pages;
//	game_free(nAllocAmount,TEXTURE_PAGES);
	GlobalFree((HGLOBAL)base);
	
	
//	if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bHardware == false)
//	{
//		InitSoftwarePalette(DXPaletteList[0]);
//	}

	// Setup Software Palette

//	if(DXD3D(App.DeviceInfoPtr,App.DXConfigPtr).bHardware == false && t16BitTextures == false)
//	{
//		InitSoftwarePalette(DXPaletteList[0]);
//	}
	
	return(1);
}

/****************************************************************************************
 *!			Load Individual Rooms and associated data
 ***************************************************************************************/

//extern void "C" BuildOutsideTable();

int LoadRooms(HANDLE file)
{
	/* Load room data from file. Return 1 if OK, 0 if failure */
	int i;
	sint16 ndoors;
	sint32 size;
	ROOM_INFO *r;
	DWORD read;

	/* Read in actual room data */
	MyReadFile(file, &number_rooms, sizeof(number_rooms), &read, NULL);

	if (number_rooms<0 || number_rooms>MAX_ROOMS)
	{
		lstrcpy(exit_message, "LoadRoom(): Too many rooms");
		return(0);
	}

	/* Allocate memory for rooms */
	room = (ROOM_INFO *)game_malloc(number_rooms * sizeof(ROOM_INFO), ROOM_INFOS);
	if (room == NULL)
	{
		lstrcpy(exit_message, "LoadRoom(): Could not allocate memory for rooms");
		return(0);
	}

	for (i=0, r=room; i<number_rooms; i++, r++)
	{
		/* Room position */
		MyReadFile(file, &r->x, sizeof(sint32), &read, NULL);
		r->y = 0;
		MyReadFile(file, &r->z, sizeof(sint32), &read, NULL);

		/* Room floor/ceiling bounds */
		MyReadFile(file, &r->minfloor, sizeof(sint32), &read, NULL);
		MyReadFile(file, &r->maxceiling, sizeof(sint32), &read, NULL);

		/* Room mesh */
		MyReadFile(file, &size, sizeof(sint32), &read, NULL);
		r->data = (sint16 *)game_malloc(size * sizeof(sint16), ROOM_MESH);
		MyReadFile(file, r->data, sizeof(sint16) * size, &read, NULL);

		/* Doors */
		MyReadFile(file, &ndoors, sizeof(sint16), &read, NULL);
		if (ndoors)
		{
			r->door = (sint16 *)game_malloc((ndoors*16+1)*sizeof(sint16), ROOM_DOOR);
			r->door[0] = ndoors;
			MyReadFile(file, &r->door[1], sizeof(sint16) * ndoors*16, &read, NULL);
		}
		else
			r->door = NULL;

		/* Floor data */
		MyReadFile(file, &r->x_size, sizeof(sint16), &read, NULL);
		MyReadFile(file, &r->y_size, sizeof(sint16), &read, NULL);
		size = (int)r->x_size * r->y_size;
		r->floor = (FLOOR_INFO *)game_malloc(size*sizeof(FLOOR_INFO), ROOM_FLOOR);
		MyReadFile(file, r->floor, sizeof(FLOOR_INFO) * size, &read, NULL);

		/* Light data */
		MyReadFile(file, &r->ambient, sizeof(sint16), &read, NULL);
//		MyReadFile(file, &r->ambientB, sizeof(sint16), &read, NULL);
		MyReadFile(file, &r->lighting, sizeof(sint16), &read, NULL);
		MyReadFile(file, &r->num_lights, sizeof(sint16), &read, NULL);
		if (r->num_lights)
		{
			r->light = (LIGHT_INFO *)game_malloc( r->num_lights * sizeof(LIGHT_INFO),ROOM_LIGHTS );
			MyReadFile(file, r->light, sizeof(LIGHT_INFO) * r->num_lights, &read, NULL);
		}
		else
		{
#ifdef GAMEDEBUG
			Log(LT_Warning,"No Lights in Room %d\n",i );
#endif
			r->light=NULL;
		}

		/* Static mesh objects */
		MyReadFile(file, &r->num_meshes, sizeof(sint16), &read, NULL);
		if (r->num_meshes)
		{
			r->mesh = (MESH_INFO *)game_malloc( r->num_meshes * sizeof(MESH_INFO),ROOM_STATIC_MESH_INFOS );
			MyReadFile(file, r->mesh, sizeof(MESH_INFO) * r->num_meshes, &read, NULL);
		}
		else
			r->mesh=NULL;

		/* Load in flipped room */
		MyReadFile(file, &r->flipped_room, sizeof(sint16), &read, NULL);

		/* Load in flags (for underwater etc) */
		MyReadFile(file, &r->flags, sizeof(uint16), &read, NULL);

		/* Load Mesh Effect */
		MyReadFile(file,&r->MeshEffect,sizeof(char),&read,NULL);


		r->bound_active = 0;			// Initialise room variables.
		r->left = phd_winxmax;
		r->top = phd_winymax;
		r->right = r->bottom = 0;
		r->item_number = NO_ITEM;
		r->fx_number = NO_ITEM;
	}

	
	BuildOutsideTable();


	
	/* Load in 'floor_data' part of wad */
	MyReadFile(file, &size, sizeof(sint32), &read, NULL);
	floor_data = (sint16 *)game_malloc(size * sizeof(sint16),FLOOR_DATA );
	MyReadFile(file, floor_data, sizeof(sint16) * size, &read, NULL);

	return(1);
}

#define DO_UV_ADJUST(v,add) if (((v)&0xff)<0x80) (v)=((v)&0xff00)+(add); else (v)=((v)|0xff)-(add);

void AdjustTextureUVs(bool tNew)
{

	if (tNew)
		App.nUVAdd=0;

	int nAdd=128; //G_AppSettings.nNearestAdjustment;

//	if (G_AppSettings.nRenderMode==RENDERER_HAL)
//		{
//		if ((G_AppSettings.nTexelAdjustMode==TEXEL_ALWAYS) ||
//			(G_AppSettings.nTexelAdjustMode==TEXEL_WHENFILTERING && G_AppSettings.tBilinearFiltering))
//			nAdd=G_AppSettings.nLinearAdjustment;
//		}
	nAdd-=App.nUVAdd;

	PHDTEXTURESTRUCT* pTS=phdtextinfo;
	uint8* pUVF=LabTextureUVFlag;
	int nTI=LnTextureInfos;
	while (nTI--)
		{
		uint16* pUV=(uint16*)&(pTS->u1);
		uint8 bByte=*pUVF++;
		for (int nBit=0;nBit<8;++nBit)
			{
			if (bByte&1)
				*pUV-=nAdd;
			else
				*pUV+=nAdd;
			bByte>>=1;
			++pUV;
			}
		++pTS;
		}
	App.nUVAdd+=nAdd;
}


/**************************************************************************************************
 *!			Load the Objects   Meshes,Animation Data and object_info data
 *************************************************************************************************/
int LoadObjects(HANDLE file)
{
	int i, j;
	sint32 number,size, number_anims;
	DWORD read;

	/* Load Actual mesh data */
	MyReadFile(file, &size, sizeof(sint32), &read, NULL);
	mesh_base = (sint16 *)game_malloc(size * sizeof(sint16), MESHES);
	MyReadFile(file, mesh_base, sizeof(sint16) * size, &read, NULL);

	/* Load meshes (offset pointers first) */
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	meshes = (sint16 **)game_malloc(number * sizeof(sint16 *), MESH_POINTERS);
	MyReadFile(file, meshes, sizeof(sint16 *) * number, &read, NULL);

	/* Adjust mesh offsets using base pointer */
	for (i=0; i<number; i++)
		meshes[i] = mesh_base + (int)meshes[i]/2;

	/* Load in animation arrays */
	MyReadFile(file, &number_anims, sizeof(sint32), &read, NULL);
	anims = (ANIM_STRUCT *)game_malloc(number_anims * sizeof(ANIM_STRUCT), ANIMS);
	MyReadFile(file, anims, sizeof(ANIM_STRUCT) * number_anims, &read, NULL);

	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	changes = (CHANGE_STRUCT *)game_malloc(number * sizeof(CHANGE_STRUCT), STRUCTS);
	MyReadFile(file, changes, sizeof(CHANGE_STRUCT) * number, &read, NULL);

	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	ranges = (RANGE_STRUCT *)game_malloc(number * sizeof(RANGE_STRUCT), RANGES);
	MyReadFile(file, ranges, sizeof(RANGE_STRUCT) * number, &read, NULL);

	/* Load in animation data wodges */
	MyReadFile(file, &size, sizeof(sint32), &read, NULL);
	commands = (sint16 *)game_malloc(size * sizeof(sint16), COMMANDS);
	MyReadFile(file, commands, sizeof(sint16) * size, &read, NULL);

	/* Bones */
	MyReadFile(file, &size, sizeof(sint32), &read, NULL);
	bones = (sint32 *)game_malloc(size * sizeof(sint32), BONES);
	MyReadFile(file, bones, sizeof(sint32) * size, &read, NULL);

	/* Frames */
	MyReadFile(file, &size, sizeof(sint32), &read, NULL);
	frames = (sint16 *)game_malloc(size * sizeof(sint16),FRAMES );
	MyReadFile(file, frames, sizeof(sint16) * size, &read, NULL);

	/* Remap anim pointers */
	for (i=0; i<number_anims; i++)
		anims[i].frame_ptr = (sint16 *)((long)anims[i].frame_ptr + (long)frames);

	/* Load in Normal animating objects */
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	for (i=0; i<number; i++)
	{
		MyReadFile(file, &j, sizeof(sint32), &read, NULL);									// Read normal object number
		MyReadFile(file, &objects[j].nmeshes, sizeof(sint16), &read, NULL);
		MyReadFile(file, &objects[j].mesh_index, sizeof(sint16), &read, NULL);
		MyReadFile(file, &objects[j].bone_index, sizeof(sint32), &read, NULL);
		MyReadFile(file, &size, sizeof(sint32), &read, NULL);
		objects[j].frame_base = (sint16 *)((long)size + (long)frames);
		MyReadFile(file, &objects[j].anim_index, sizeof(sint16), &read, NULL);
		objects[j].loaded = 1;												// Flag Object as loaded
	}

	/* Initialise objects: must come after bones have been set up, but before items loaded */
	InitialiseObjects();

	/* Load in Static Objects */
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	for (i=0; i<number; i++)
	{
		MyReadFile(file, &j, sizeof(sint32), &read, NULL);										// Read Static Object Number
		MyReadFile(file, &static_objects[j].mesh_number, sizeof(sint16), &read, NULL);
		MyReadFile(file, &static_objects[j].x_minp, sizeof(sint16) * 6, &read, NULL);	// Read Physical Bound Info..
		MyReadFile(file, &static_objects[j].x_minc, sizeof(sint16) * 6, &read, NULL);	// Read Collide Bound Info..
		MyReadFile(file, &static_objects[j].flags, sizeof(sint16), &read, NULL);
	}

	/* Read the Texture Info Structures */
/*
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	if (number > MAX_TEXTURES)
	{
		lstrcpy(exit_message, "Too many Textures in level");
		return (0);
	}
	MyReadFile(file, phdtextinfo, sizeof(PHDTEXTURESTRUCT) * number, &read, NULL);

	LnTextureInfos=number;
	for (number=0;number<LnTextureInfos;++number)
		{
		uint16* pUV=(uint16*)&(phdtextinfo[number].u1);
		uint8 bByte=0;
		for (int nBit=0;nBit<8;++nBit)
			{
			if ((*pUV)&0x80)
				{
				*pUV|=0xff;
				bByte|=(1<<nBit);
				}
			else
				*pUV&=0xff00;
			++pUV;
			}
		LabTextureUVFlag[number]=bByte;
		}
	AdjustTextureUVs(true);
*/
	return(1);
}


/******************************************************************************
 *!			Load sprite infos and game data
 *****************************************************************************/
int LoadSprites(HANDLE file)
{
	int i, j;
	int number;
	DWORD read;

	/* Sprite info pointers */
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	MyReadFile(file, phdsprinfo, sizeof(PHDSPRITESTRUCT) * number, &read, NULL);

	/* Load object information (for non-static sprites) */
	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	for (i=0; i<number; i++)
	{
		/* Only some objects are represented in each wad: this is the object number */
		MyReadFile(file, &j, sizeof(sint32), &read, NULL);

		/* Wad file may contain information about static objects too (useful for editors,
			but game code doesn't want to know) which needs skipping across */
		if (j < NUMBER_OBJECTS)
		{
			/* 'nmeshes' is a negative value for sprites, and stores number of animated frames */
			MyReadFile(file, &objects[j].nmeshes, sizeof(sint16), &read, NULL);
			MyReadFile(file, &objects[j].mesh_index, sizeof(sint16), &read, NULL);
			objects[j].loaded = 1;
		}
		else
		{
		/* NOTE: Following routine is just a hack to load in mesh number into
		         the STATIC_INFO structure - doesn't get bounding info or anything */

			/* Load in data for STATIC_INFO structures */
			j -= NUMBER_OBJECTS;
			SetFilePointer(file, sizeof(sint16), NULL, FILE_CURRENT);
			MyReadFile(file, &static_objects[j].mesh_number, sizeof(sint16), &read, NULL);
		}
	}
	return(1);
}

/****************************************************************************************
 *			Load all Items on the Level and initialise them...
 ***************************************************************************************/
int LoadItems(HANDLE file)
{
	/* Allocate and load in all the starting positions of movable items from room data */
	sint16 i;
	sint32 number_items;
	ITEM_INFO *item;
	DWORD read;

	MyReadFile(file, &number_items, sizeof(sint32), &read, NULL);

	/* Actual final game will crash and burn if 'number_items' is 0, but may be during testing */
	if (number_items == 0)
		return(1);

	if (number_items > NUMBER_ITEMS)
	{
		lstrcpy(exit_message, "LoadItems(): Too Many Items being Loaded!!");
		return(0);
	}

	items = (ITEM_INFO *)game_malloc(sizeof(ITEM_INFO) * NUMBER_ITEMS, ITEMS);
	if (items == NULL)
	{
		lstrcpy(exit_message, "LoadItems(): Unable to allocate memory for 'items'");
		return(0);
	}

	level_items = number_items;

	InitialiseItemArray(NUMBER_ITEMS);

	for (i=0; i<number_items; i++)
	{
		item = &items[i];
		MyReadFile(file, &item->object_number, sizeof(sint16), &read, NULL);
		MyReadFile(file, &item->room_number, sizeof(sint16), &read, NULL);
		MyReadFile(file, &item->pos.x_pos, sizeof(sint32), &read, NULL);
		MyReadFile(file, &item->pos.y_pos, sizeof(sint32), &read, NULL);
		MyReadFile(file, &item->pos.z_pos, sizeof(sint32), &read, NULL);
		MyReadFile(file, &item->pos.y_rot, sizeof(sint16), &read, NULL);
		MyReadFile(file, &item->shade, sizeof(sint16), &read, NULL);
		MyReadFile(file, &item->shadeB, sizeof(sint16), &read, NULL); // two shades for item_info
		MyReadFile(file, &item->flags, sizeof(sint16), &read, NULL);

		if (item->object_number<0 || item->object_number>=NUMBER_OBJECTS)
		{
			wsprintf(exit_message, "LoadItems(): Bad Object number (%d) on Item %d", item->object_number, i);
			return (0);
		}

		/* NOTE: Any extra memory allocation for item is done via InitialiseItem now */
		InitialiseItem(i);
	}

	return(1);
}

/*****************************************************************************
 *				Load the depthQ table for 3DSystem
 ****************************************************************************/
int LoadDepthQ(HANDLE file)
{

	
	int i, j;
	DWORD read;

	MyReadFile(file, depthq_table, 32*256, &read, NULL);

	// Force colour 0 to black 
	for (i=0; i<33; i++)
		depthq_table[i][0] = 0;

//	if (G_RenderInfo.tUse236Palette)
//		{
//		uint8 abNewPalette[768];

//		SWR_Make236Palette(game_palette,(uint8*)depthq_table,32*256,abNewPalette);
//		SWR_ConvertImage((uint8*)depthq_table,256,32,256,game_palette,(uint8*)depthq_table,256,abNewPalette,true);  // probably best to avoid windows colours due to water palette...
//		memcpy(game_palette,abNewPalette,768);

		// create the transparency table (entry 32 of the depthq table)
//		uint8* pPal=game_palette;
//		for (int nEntry=0;nEntry<256;++nEntry,pPal+=3)
//			depthq_table[32][nEntry]=(char)S_COLOUR(pPal[0]>>1,pPal[1]>>1,pPal[2]>>1);
//		}
//	else
		memcpy(depthq_table[32],depthq_table[24],256);


	for (i=0; i<32; i++)
	{
		for (j=0; j<256; j++)
			gouraud_table[j][i] = depthq_table[i][j];
	}

	// Calculate water palette 
	wet = 0;
	for (i=0; i<256*3; i+=3)
	{
		j = (int)((unsigned int)game_palette[i]*2)/3;
		water_palette[i] = j;
		j = (int)((unsigned int)game_palette[i+1]*2)/3;
		water_palette[i+1] = j;
		water_palette[i+2] = game_palette[i+2];
	}

	return (1);
}

/*****************************************************************************
 *			Load The Palette and do associated stuff
 *****************************************************************************/
int LoadPalette(HANDLE file)
{
	int i;
	DWORD read;

	MyReadFile(file, game_palette, 256*3, &read, NULL);

	/* Force colour 0 to black */
	game_palette[0] = game_palette[1] = game_palette[2] = 0;

	for (i=3;i<768;++i)
		{
		game_palette[i]<<=2;
		//Log("%d",game_palette[i]);
		}

	MyReadFile(file,G_GouraudPalette,256*4,&read,NULL);

	return(1);
}

/****************************************************************************************
 *			Load the Cameras  for level
 ***************************************************************************************/
int LoadCameras(HANDLE file)
{
	/* Load all the stuff to do with camera positions and the like */
	DWORD read;

	MyReadFile(file, &number_cameras, sizeof(sint32), &read, NULL);
	if (number_cameras == 0)
		return (1);

	camera.fixed = (OBJECT_VECTOR *)game_malloc(sizeof(OBJECT_VECTOR) * number_cameras, CAMERAS);
	if (camera.fixed == NULL)
		return (0);

	MyReadFile(file, camera.fixed, sizeof(OBJECT_VECTOR) * number_cameras, &read, NULL);

	return (1);
}

/***************************************************************************************
 * Load in the atmospheric sounds effects that Lara will here as she strolls about
 **************************************************************************************/
int LoadSoundEffects(HANDLE file)
{
	DWORD read;

	MyReadFile(file, &number_sound_effects, sizeof(sint32), &read, NULL);
	if (number_sound_effects == 0)
		return (1);

	sound_effects = (OBJECT_VECTOR *)game_malloc(sizeof(OBJECT_VECTOR) * number_sound_effects, SOUND_FX);
	if (sound_effects == NULL)
		return (0);

	MyReadFile(file, sound_effects, sizeof(OBJECT_VECTOR) * number_sound_effects, &read, NULL);

	return (1);
}

/***************************************************************************************
 *					Load in box information
 **************************************************************************************/

int LoadBoxes(HANDLE file)
{
	sint32 i, j, number_overlaps;
	DWORD read;

	MyReadFile(file, &number_boxes, sizeof(sint32), &read, NULL);
	boxes = (BOX_INFO *)game_malloc(sizeof(BOX_INFO) * number_boxes, BOXES);
	MyReadFile(file, boxes, sizeof(BOX_INFO) * number_boxes, &read, NULL);
	if (read != sizeof(BOX_INFO) * number_boxes)
	{
		lstrcpy(exit_message, "LoadBoxes(): Unable to load boxes");
		return (0);
	}

	/* Box overlaps */
	MyReadFile(file, &number_overlaps, sizeof(sint32), &read, NULL);
	overlap = (uint16 *)game_malloc(sizeof(uint16) * number_overlaps, OVERLAPS);

	MyReadFile(file, overlap, sizeof(uint16) * number_overlaps, &read, NULL);
	if (read != sizeof(uint16) * number_overlaps)
	{
		lstrcpy(exit_message, "LoadBoxes(): Unable to load box overlaps");
		return (0);
	}


	/* Zones (version for flipped/unflipped versions of map) */



	for (i=0; i<2; i++)
	{
		for (j=0; j<4; j++)
		{
			// NEWCODE: Check SPIDER or BANDIT3 loaded before allocating ground_zone[1]
			//				Check YETI or WORKER3 loaded before allocating ground_zone[3] 
			//				Never load ground_zone[2] as never used 
			//if (j==2 || (j==1 && !objects[SPIDER].loaded && !objects[BANDIT3].loaded) ||
			//				(j==3 && !objects[YETI].loaded && !objects[WORKER3].loaded))
			//	SetFilePointer(file, sizeof(sint16) * number_boxes, 0, FILE_CURRENT);
			//else
			{
				ground_zone[j][i] = (sint16 *)game_malloc(sizeof(sint16) * number_boxes, GROUNDZONE);
				MyReadFile(file, ground_zone[j][i], sizeof(sint16) * number_boxes, &read, NULL);
				if (read != sizeof(sint16) * number_boxes)
				{
					lstrcpy(exit_message, "LoadBoxes(): Unable to load 'ground_zone'");
					return (0);
				}
			}
		}


		fly_zone[i] = (sint16 *)game_malloc(sizeof(sint16) * number_boxes, FLYZONE);
		MyReadFile(file, fly_zone[i], sizeof(sint16) * number_boxes, &read, NULL);
		if (read != sizeof(sint16) * number_boxes)
		{
			lstrcpy(exit_message, "LoadBoxes(): Unable to load 'fly_zone'");
			return (0);
		}
	}


	return (1);
}


/**************************************************************************************************
 *!							Load animating textures
 *************************************************************************************************/
int LoadAnimatedTextures(HANDLE file)
{
	int size;
	DWORD read;

	MyReadFile(file, &size, sizeof(sint32), &read, NULL);

	aranges = (sint16 *)game_malloc( size*sizeof(sint16), ANIMATING_TEXTURE_RANGES);
	MyReadFile(file, aranges, sizeof(sint16) * size, &read, NULL);


	sint32 number;

	MyReadFile(file, &number, sizeof(sint32), &read, NULL);
	if (number > MAX_TEXTURES)
	{
		lstrcpy(exit_message, "Too many Textures in level");
		return (0);
	}
	MyReadFile(file, phdtextinfo, sizeof(PHDTEXTURESTRUCT) * number, &read, NULL);

	LnTextureInfos=number;
	for (number=0;number<LnTextureInfos;++number)
		{
		uint16* pUV=(uint16*)&(phdtextinfo[number].u1);
		uint8 bByte=0;
		for (int nBit=0;nBit<8;++nBit)
			{
			if ((*pUV)&0x80)
				{
				*pUV|=0xff;
				bByte|=(1<<nBit);
				}
			else
				*pUV&=0xff00;
			++pUV;
			}
		LabTextureUVFlag[number]=bByte;
		}
	AdjustTextureUVs(true);

	return(1);
}


/*****************************************************************************
 *			Load Camera Frame Sequence data for Cutscene
 ****************************************************************************/
int LoadCinematic(HANDLE file)
{
	DWORD read;

	MyReadFile(file, &num_cine_frames,sizeof(sint16), &read, NULL);
	if (num_cine_frames)
	{
		cine = (sint16 *)game_malloc( sizeof(sint16)*8 * num_cine_frames, CINEMATIC_FRAMES);
		MyReadFile(file, cine, sizeof(sint16)*8 * num_cine_frames, &read, NULL);
		cine_loaded = 1;
	}
	else
		cine_loaded = 0;

	return(1);
}


/******************************************************************************
 *					Load demo data; either from wad or .DEM file
 *****************************************************************************/
int LoadDemo(HANDLE file)
{
	sint16 length;
	DWORD read;

	democount = 0;
	demoptr = (uint32 *)game_malloc(DEMOCOUNT_MAX*sizeof(uint32), LOADDEMO_BUFFER);

	MyReadFile(file, &length, sizeof(sint16), &read, NULL);
	if (length)
	{
		MyReadFile(file, demoptr, length, &read, NULL);
		demo_loaded = 1;
	}
	else
		demo_loaded = 0;		    

	return(1);
}


void LoadDemFile(char *filename)
{
	/* Load demo data from a .DEM file if it exists (will overwrite the standard wad demo) */
	HANDLE file;
	DWORD read;
	char fname2[80];

	strcpy(fname2,filename);
	build_ext(fname2, "DEM");

	file = CreateFile(fname2, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
		return;

	MyReadFile(file, demoptr, DEMOCOUNT_MAX*sizeof(uint32), &read, NULL);
	demo_loaded = (read > 0);

	CloseHandle(file);
	Log(LT_Info,"Found Demo Data '%s'", fname2);
}

int LoadSamples(HANDLE file)
	{
	Log(LT_Enter,"LoadSamples");

	sound_active=false;
	if (!DS_IsSoundEnabled())
		return true;

	DS_FreeAllSamples();

	DWORD read;
	// Get sample look up table
	MyReadFile(file,&sample_lut[0],sizeof(sint16)*MAX_SAMPLES,&read,NULL);

	// Get number of sample infos
	MyReadFile(file,&num_sample_infos,sizeof(int),&read,NULL);
	if (num_sample_infos==0)
		{
		Log(LT_Error,"No sample infos");
		return false;
		}
	
	// Get sample infos
	sample_infos=static_cast<SAMPLE_INFO*>(game_malloc(sizeof(SAMPLE_INFO)*num_sample_infos,SAMPLEINFOS));
	MyReadFile(file,sample_infos,sizeof(SAMPLE_INFO)*num_sample_infos,&read,NULL);

	for (int i=0;i<num_sample_infos;++i)
		{
		static char* apStr[4]={"WAIT_SOUND","RESTART_SOUND","AMBIENT_SOUND","eh?"};
		SAMPLE_INFO* pSI=sample_infos+i;
		Log(LT_Sound,"Sample %d: %s",i,apStr[pSI->flags&3]);
		}
	
	// Get number of samples
	int nSamples;
	MyReadFile(file,&nSamples,sizeof nSamples,&read,NULL);
	if (nSamples==0)
		{
		Log(LT_Error,"No samples");
		return false;
		}

#ifndef DEMO
	// Load table of samples required
	int required_samples[500];
	MyReadFile(file, required_samples, sizeof(int)*nSamples, &read, NULL);

	// Open sound effects wad
#ifdef CD_LOAD
	HANDLE sfx = CreateFile(GetFullPath("data\\main.sfx"), GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#else
	HANDLE sfx = CreateFile("data\\main.sfx", GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
#endif
	if (sfx == INVALID_HANDLE_VALUE)
	{
		wsprintf(exit_message, "Could not open MAIN.SFX file");
		return (0);
	}

	// Load each sample from the sample wad
	int required=0, sample=0;
	char* pWaveData;
	DWORD dwHeader[11], dwWaveLength, dwStoredLength;
	WAVEFORMATEX* pWF = reinterpret_cast<WAVEFORMATEX*>(dwHeader+5);
	while (required<nSamples)
	{
		MyReadFile(sfx, dwHeader, sizeof dwHeader, &read, NULL);
		if (dwHeader[0]!=MAKEFOURCC('R','I','F','F') || 
			dwHeader[2]!=MAKEFOURCC('W','A','V','E') ||
			dwHeader[9]!=MAKEFOURCC('d','a','t','a'))
		{
			Log(LT_Error,"Sample %d: Bad wave format",sample);
			return false;
		}
		pWF->cbSize = 0;
		dwWaveLength = dwHeader[10];
		dwStoredLength = (dwWaveLength+1)&(~1);
		
		// do we want this sample, or should we skip it?
		if (required_samples[required] != sample)
			SetFilePointer(sfx, dwStoredLength, 0, FILE_CURRENT);
		else
		{
			pWaveData = static_cast<char*>(game_malloc(dwStoredLength, SAMPLES));
			MyReadFile(sfx, pWaveData, dwStoredLength, &read, NULL);
			if (!DS_MakeSample(required, pWF, pWaveData, dwWaveLength))
			{
				Log(LT_Error,"Sample %d: DS_MakeSample failed", required);
				return false;
			}
			game_free(dwStoredLength, SAMPLES);

			required++; // go onto next sample
		}

		sample++;
	}

	CloseHandle(sfx);
#else
	// Samples stored at end of wad (for HD demo version)

	// Load the samples
	char* pWaveData;
	DWORD dwHeader[11],dwWaveLength,dwStoredLength;
	WAVEFORMATEX* pWF=reinterpret_cast<WAVEFORMATEX*>(dwHeader+5);
	int nSample;
	for (nSample=0;nSample<nSamples;++nSample)
		{
		MyReadFile(file,dwHeader,sizeof dwHeader,&read,NULL);
		if (dwHeader[0]!=MAKEFOURCC('R','I','F','F') ||
			dwHeader[2]!=MAKEFOURCC('W','A','V','E') ||
			dwHeader[9]!=MAKEFOURCC('d','a','t','a'))
			{
			Log(LT_Error,"Sample %d: Bad wave format",nSample);
			return false;
			}
		pWF->cbSize=0;
		dwWaveLength=dwHeader[10];
		dwStoredLength=(dwWaveLength+1)&(~1);
		pWaveData=static_cast<char*>(game_malloc(dwStoredLength,SAMPLES));
		MyReadFile(file,pWaveData,dwStoredLength,&read,NULL);
		if (!DS_MakeSample(nSample,pWF,pWaveData,dwWaveLength))
			{
			Log(LT_Error,"Sample %d: DS_MakeSample failed",nSample);
			return false;
			}
		game_free(dwStoredLength,SAMPLES);
		}
	if (nSamples!=nSample)
		{
		Log(LT_Error,"nSamples!=nSample");
		return false;
		}
#endif
	Log(LT_Info,"Loaded %d samples",nSamples);
	sound_active=true;
	return true;
	}

/******************************************************************************
 *					Build filename Extension.....
 *****************************************************************************/
void	build_ext( char *fname, char *ext )
{
	while ( *fname!='.' && *fname!=0 )		// Dont Use Directories with . in Name !!!
		fname++;

	*(fname++) = '.';
	*(fname++) = *(ext++);
	*(fname++) = *(ext++);
	*(fname++) = *(ext++);
	*(fname++) = 0;
}

/****************************************************************************
 *				Get Full Path of where to Load Data from
 ***************************************************************************/
char	*GetFullPath( char *filename )
{
	static	char	newpath[128];

#ifdef DEMO
	wsprintf( newpath,"%c:\\tomb2\\%s",cd_drive,filename );		// Get Path to CD-ROM drive subdir
#else
	wsprintf( newpath,"%c:\\%s",cd_drive,filename );		// Get Path to CD-ROM drive..
#endif

	return( newpath );
}


/****************************************************************************
 *				Test CD Drive please..
 ***************************************************************************/
int FindCdDrive(void)
{
	unsigned int type;
	char root[5];
#ifdef DEMO
	char file_check[] = "D:\\tomb2\\data\\demopc.dat";
#else
	char file_check[] = "D:\\data\\legal.pcx";
#endif
	DWORD drives;
	HANDLE file;

	// WIN95: Bit mask of which drives available
	drives = GetLogicalDrives();

	cd_drive = 'A';
	lstrcpy(root, "A:\\");
	while (drives)
	{
		// WIN95: If drive letter exists, check out if it is a CD drive
		if (drives & 1)
		{
			root[0] = cd_drive;

			type = GetDriveType(root);
			if (type == DRIVE_CDROM)
			{
				// NOTE: user may have more than one CD drive, so need to check this has Tomb 2 in it
				file_check[0] = cd_drive;
				file = CreateFile(file_check, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
				if (file != INVALID_HANDLE_VALUE)
				{
					CloseHandle(file);
					return 1;
				}
			}
		}

		// Go on to next drive letter
		cd_drive++;
		drives >>= 1;
	}

	return (0);
}

#ifdef GAMEDEBUG
/******************************************************************************
 ******************************************************************************
 *						Demo playback stuff
 ******************************************************************************
 *****************************************************************************/
/*	char	*demonames[] = {
				"demodata\\gym.dem",
				"demodata\\level1.dem",
				"demodata\\level2.dem",
				"demodata\\level3a.dem",
				"demodata\\level3b.dem",
				"demodata\\level4.dem",
				"demodata\\level5.dem",
				"demodata\\level6.dem",
				"demodata\\level7a.dem",
				"demodata\\level7b.dem",
				"demodata\\level8a.dem",
				"demodata\\level8b.dem",
				"demodata\\level8c.dem",
				"demodata\\level10a.dem",
				"demodata\\level10b.dem",
				"demodata\\level10c.dem",
	};
*/
/*******************************************************************************
 *				Initialise Recording of Demo
 ******************************************************************************/
void	S_InitSaveDemo( void )
{
	democount = 0;
	demoptr = (uint32 *)game_malloc( DEMOCOUNT_MAX*sizeof(uint32),SAVEDEMO_BUFFER );
}

/******************************************************************************
 *				Save Demo Data so far
 *****************************************************************************/
void	S_SaveDemoData( int level_number )
{
	HANDLE file;
	DWORD write;
	char filename[30];
	Log(LT_Info,"Saving Demo Data %d",level_number);

	if (democount > DEMOCOUNT_MAX-1)
		democount = DEMOCOUNT_MAX-1;

	*(demoptr+democount) = (unsigned long)-1;
	democount++;
	strcpy(filename,GF_levelfilenames[level_number]);
	//wsprintf(filename, "DATA\\%s", GF_Level_Names[level_number]);
	build_ext(filename, "DEM");

	file = CreateFile(filename, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file != INVALID_HANDLE_VALUE)
	{
		WriteFile(file, demoptr, democount * sizeof(uint32), &write, NULL);
		CloseHandle(file);
	}
}


/*********************************************************************
 *		Allows Command Line Demo Recording
 ********************************************************************/
/*int		GetLevelNum( char *levname )
{
	if ( !strcmp("gym",levname) || !strcmp("g",levname) || !strcmp("assault",levname) )
		return(LV_GYM);
	if ( !strcmp("level1",levname) || !strcmp("1",levname) || !strcmp("wall",levname) )
		return(LV_LEVEL1);
	if ( !strcmp("level2",levname) || !strcmp("2",levname) || !strcmp("boat",levname) )
		return(LV_LEVEL2);
	if ( !strcmp("level3",levname) || !strcmp("3",levname) || !strcmp("venice",levname) )
		return(LV_LEVEL3);
	if ( !strcmp("level4",levname) || !strcmp("4",levname) || !strcmp("opera",levname) )
		return(LV_LEVEL4);
	if ( !strcmp("level5",levname) || !strcmp("5",levname) || !strcmp("rig",levname) )
		return(LV_LEVEL5);
	if ( !strcmp("level6",levname) || !strcmp("6",levname) || !strcmp("unwater",levname) )
		return(LV_LEVEL6);
	if ( !strcmp("level7",levname) || !strcmp("7",levname) || !strcmp("keel",levname) )
		return(LV_LEVEL7);
	if ( !strcmp("level8",levname) || !strcmp("8",levname) || !strcmp("living",levname) )
		return(LV_LEVEL8);
	if ( !strcmp("level9",levname) || !strcmp("9",levname) || !strcmp("dock",levname) )
		return(LV_LEVEL9);
	if ( !strcmp("level10",levname) || !strcmp("10",levname) || !strcmp("skidoo",levname) )
		return(LV_LEVEL10);
	if ( !strcmp("level11",levname) || !strcmp("11",levname) || !strcmp("monastry",levname) )
		return(LV_LEVEL11);
	if ( !strcmp("level12",levname) || !strcmp("12",levname) || !strcmp("catacomb",levname) )
		return(LV_LEVEL12);
	if ( !strcmp("level13",levname) || !strcmp("13",levname) || !strcmp("icecave",levname) )
		return(LV_LEVEL13);
	if ( !strcmp("level14",levname) || !strcmp("14",levname) || !strcmp("emprtomb",levname) )
		return(LV_LEVEL14);
	if ( !strcmp("level15",levname) || !strcmp("15",levname) || !strcmp("floating",levname) )
		return(LV_LEVEL15);
	if ( !strcmp("level16",levname) || !strcmp("16",levname) || !strcmp("house",levname) )
		return(LV_LEVEL16);
	return(0);
} */
#endif

/******************************************************************************
 ******************************************************************************
 *			Load Actual Level data given Filename
 ******************************************************************************
 *****************************************************************************/

int LoadLevel( char *filename, int req_level )
{
	DWORD read;
	HANDLE file;
	int version, level;

#ifdef CD_LOAD
  	filename = GetFullPath( filename );
#endif
	strcpy(LacLevelFileName,filename);

	init_game_malloc();

	// WIN95: open wad file
	file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL|FILE_FLAG_SEQUENTIAL_SCAN, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		wsprintf(exit_message, "LoadLevel(): Could not open %s (level %d)", filename, req_level);
		return (0);
	}

	/* Check Version Number */

	MyReadFile(file, &version, sizeof(int), &read, NULL);
/*
	if (version != OBJECT_VERSION) // RF 
	{
		if (version < OBJECT_VERSION)
			wsprintf(exit_message, "FATAL: Level %d (%s) is OUT OF DATE (version %d). COPY NEW EDITORS AND REMAKE LEVEL", req_level, filename, version);
		else
			wsprintf(exit_message, "FATAL: Level %d (%s) requires a new TOMB2.EXE (version %d) to run", req_level, filename, version);
		return (0);
	}
*/
	/* Load Palette */
	LdwPaletteOffset=SetFilePointer(file,0,0,FILE_CURRENT);
	if (!LoadPalette(file))
		return(0);

	/* Load All Texture Pages */
	LdwTextureOffset=SetFilePointer(file,0,0,FILE_CURRENT);
	if (!LoadTexturePages(file))
		return(0);

	/* Now Load level number and see if it is Correct */
	MyReadFile(file, &level, sizeof(int), &read, NULL);
//	if ( level!=req_level )
//	{
//		sprintf(exit_message, "FATAL ERROR: Level data corrupt"); // bit of a lie
//		S_ExitSystem(exit_message);
//	}

	/* Load Rooms and Associated data */
	if (!LoadRooms(file))
		return(0);

	/* Load Objects */
	if (!LoadObjects(file))
		return(0);

	/* Load Sprite Infos and data */
	if (!LoadSprites(file))
		return(0);

#ifdef DEMO
	/* Load DEPTHQ table early to confound piracy */
	LdwDepthQOffset=SetFilePointer(file,0,0,FILE_CURRENT);
	if (!LoadDepthQ(file))
		return(0);
#endif

	/* Load camera position data */
	if (!LoadCameras(file))
		return (0);

	/* Load sound effect emitter info */
	if (!LoadSoundEffects(file))
		return (0);

	/* Load box information just before very, very end of room wad */
	if (!LoadBoxes(file))
		return (0);

	/* Load ranges for room texture animation slightly after just before very, very end of room wad */
	if (!LoadAnimatedTextures(file))
		return (0);

	/* Load non-static items from the end of the room wad */
	if (!LoadItems(file))
		return (0);

#ifndef DEMO
	/* Load depth cue table at very, very end of room wad */
	LdwDepthQOffset=SetFilePointer(file,0,0,FILE_CURRENT);
	if (!LoadDepthQ(file))
		return(0);
#endif

	if (!LoadCinematic(file))
		return(0);

	if (!LoadDemo(file))
		return(0);

#ifdef DUDE_DEMO
	sound_active=false;
#else
	/* Load Samples and sample infos */
	if (!LoadSamples(file))
		return(0);
#endif

	/* Load a separate demo data file for the level if it exists */
	LoadDemFile(filename);

	CloseHandle(file);

	return (1);
}

/*****************************************************************************
 *						Load Level
 ****************************************************************************/
int 	S_LoadLevelFile( char *fname, int level_number, int type )
{
#ifdef GAMEDEBUG
	Log(LT_Info,"S_LoadLevelFile(%s,%d)",fname,level_number);
#endif
	S_UnloadLevelFile();

	bool flag = false;

	if (type==IL_TITLE)
	{
				
	}	
	else if (type!=IL_QUIET && (type!=IL_CUTSCENE || GF_Playing_Story))
	{
		LoadPicture(GF_picfilenames[GF_LoadingPic],App.lpPictureBuffer,1);
		FadePictureUp();
		flag = true;
	}
	
	int option = LoadLevel( fname, level_number );

	if(flag)
		FadePictureDown();

	return(option);
}

void S_UnloadLevelFile(void)
{

	Log(LT_Enter,"S_UnloadLevelFile");
	if (App.nRenderMode==RENDERER_HAL)	// RF
		HWR_FreeTexturePages();
	for (int i=0;i<MAX_TEXTPAGES;++i)
		texture_page_ptrs[i]=0;
	LacLevelFileName[0]=0;
	LnTextureInfos=0;

}

void S_AdjustTexelCoordinates(void)
	{
	Log(LT_Enter,"S_AdjustTexelCoordinates");
	if (LnTextureInfos)
		AdjustTextureUVs(false);
	}

int S_ReloadLevelGraphics(int tPalDPQ,int tTPages)
	{
	HANDLE file;

	Log(LT_Enter,"S_ReloadLevelGraphics (level='%s')",LacLevelFileName);
	if (LacLevelFileName[0])
		{
		file=CreateFile(LacLevelFileName,GENERIC_READ,0,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,NULL);
		if (file==INVALID_HANDLE_VALUE)
			{
			Log("-> Invalid file handle");
			return 0;
			}
		if (tPalDPQ && (App.nRenderMode==RENDERER_INTERNAL)) // RF
			{
			Log("-> Reloading palette & depthq table");
			SetFilePointer(file,LdwPaletteOffset,0,FILE_BEGIN);
			LoadPalette(file);
			SetFilePointer(file,LdwDepthQOffset,0,FILE_BEGIN);
			LoadDepthQ(file);
			}
		if (tTPages)
			{
			Log("-> Reloading textures");
			if (App.nRenderMode==RENDERER_HAL) // RF
				HWR_FreeTexturePages();
			SetFilePointer(file,LdwTextureOffset,0,FILE_BEGIN);
			LoadTexturePages(file);		// no alloc
			}
		CloseHandle(file);
		}
	else
		Log("-> No level loaded");
	if (tPalDPQ)
		InitColours();	// setup inv_colours for front end & health bars...
	return 1;
	}

/******************************************************************************
 ******************************************************************************
 *			GameFlow Load Stuff						J.Gosling 1997
 ******************************************************************************
 *****************************************************************************/
/*
#define READ_STRINGS(ptrs,buffer) \
	MyReadFile(file, &number, sizeof(sint16), &read, NULL); \
	MyReadFile(file, &size, sizeof(sint16), &read, NULL); \
	buffer = (char*)GlobalAlloc(GMEM_FIXED, number*size); \
	if (!buffer) { \
		Log(LT_Info,"GF:	Could not allocate memory for strings (%d bytes)", number*size); \
		return (0); } \
	MyReadFile(file, buffer, size*number, &read, NULL ); \
	for (i=0;i<number;i++) {\
		ptrs[i] = buffer + i*size;\
		Log(LT_Info,"GF:	%d: %s", i,ptrs[i]); \
	}
*/


#if CODER==JASON
#ifdef GAMEDEBUG
char *seqcom_names[]={
	"PICTURE:",
	"LIST START >>>",
	"LIST END <<<<<",
	"FMV:",
	"STARTLEVEL:",
	"CUTSCENE:",
	"LEVCOMPLETE",
	"DEMOPLAY:",
	"JUMPTO_SEQ:",
	"END SEQUENCE",
	"SET CD TRACK:",
	"SUNSET ON",
	"LOADING PIC",		//PSX only
};
#endif
#endif


sint16 LGF_offsets[200];
						  
int	Read_Strings( int num_strings, char **strings, char **buffer, DWORD *read, HANDLE file)
{
	int	i;
	sint16 size;
	char *strptr;

#if CODER==JASON
	Log(LT_Info,"GF:		Num strings=%d", num_strings);
#endif
	MyReadFile(file, LGF_offsets, num_strings*sizeof(sint16), read, NULL);

//	for (i=0;i<num_strings;i++)
//	{
//		MyReadFile(file, &LGF_offsets[i], sizeof(sint16), read, NULL);
//		Log(LT_Info,"%d:	%d",i,LGF_offsets[i]);
//	}

	MyReadFile(file,&size,sizeof(sint16), read, NULL);

	*buffer = (char*)GlobalAlloc(GMEM_FIXED, size);
#if CODER==JASON
	Log(LT_Info,"GF:		String buffer size=%d", size);
#endif
	if (!buffer)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for strings (%d bytes)", size);
		return (0); 
	}
	MyReadFile(file, *buffer, size, read, NULL );
	if (gameflow.cyphered_strings)
	{
		// decypher string data
		strptr = *buffer;
		for (i=0;i<size;i++)
			*(strptr+i) ^= gameflow.cypher_code;
	}
	// Setup string ptrs
	for (i=0;i<num_strings;i++)
	{
		strings[i] = *buffer +LGF_offsets[i];
#if CODER==JASON
		Log(LT_Info,"GF:			%d:	%d	%s",i, LGF_offsets[i], strings[i]);
#endif
	}
	return(1);
}
		   
#if CODER==JASON
#ifdef GAMEDEBUG
void log_seq(sint16 *seq)
{
	int	i;

	if (!seq)
	{
		Log(LT_Info,"ERROR: No sequence!!!\n\n");
		return;
	}

	for(i=0;i<1000;i++)
	{
		switch(*(seq+i))
		{
			case GFE_PICTURE:
				Log(LT_Info,"GF:			%d %d PICTURE: %s", *(seq+i), *(seq+i+1), GF_picfilenames[*(seq+i+1)]);
				i++;
				break;
			case GFE_LIST_START:
				Log(LT_Info,"GF:			%d LIST START>>>", *(seq+i));
				break;
			case GFE_LIST_END:
				Log(LT_Info,"GF:			%d LIST END <<<<", *(seq+i) );
				break;
			case GFE_PLAYFMV:
				Log(LT_Info,"GF:			%d %d FMV: %s", *(seq+i), *(seq+i+1), GF_fmvfilenames[*(seq+i+1)]);
				i++;
				break;
			case GFE_STARTLEVEL:
				Log(LT_Info,"GF:			%d %d STARTLEVEL: %s", *(seq+i), *(seq+i+1), GF_Level_Names[*(seq+i+1)]);
				i++;
				break;
			case GFE_CUTANGLE:
				Log(LT_Info,"GF:			%d %d CUT ANGLE: 0x%x", *(seq+i), *(seq+i+1), *(seq+i+1));
				i++;
				break;
			case GFE_CUTSCENE:
				Log(LT_Info,"GF:			%d %d CUTSCENE: %s", *(seq+i), *(seq+i+1), GF_cutscenefilenames[*(seq+i+1)]);
				i++;
				break;
			case GFE_LEVCOMPLETE:
				Log(LT_Info,"GF:			%d LEVCOMPLETE", *(seq+i) );
				break;
			case GFE_GAMECOMPLETE:
				Log(LT_Info,"GF:			%d GAMECOMPLETE", *(seq+i) );
				break;
			case GFE_DEMOPLAY:
				Log(LT_Info,"GF:			%d %d DEMOPLAY: %s", *(seq+i), *(seq+i+1), GF_levelfilenames[*(seq+i+1)]);
				i++;
				break;
			case GFE_JUMPTO_SEQ:
				Log(LT_Info,"GF:			%d %d JUMPTOSEQ: %d", *(seq+i), *(seq+i+1), *(seq+i+1));
				i++;
				break;
			case GFE_END_SEQ:
				Log(LT_Info,"GF:			%d ENDSEQUENCE", *(seq+i) );
				Log(LT_Info,"");
				return;
			case GFE_SETTRACK:
				Log(LT_Info,"GF:			%d %d SETTRACK: %d", *(seq+i), *(seq+i+1), *(seq+i+1));
				i++;
				break;
			case GFE_SUNSET:
				Log(LT_Info,"GF:			%d SUNSET ENABLED", *(seq+i) );
				break;
			case GFE_LOADINGPIC:
				Log(LT_Info,"GF:			%d %d LOADING PIC: %s", *(seq+i), *(seq+i+1), GF_picfilenames[*(seq+i+1)]);
				i++;
				break;
			case GFE_DEADLY_WATER:
				Log(LT_Info,"GF:			%d DEADLY WATER", *(seq+i) );
				break;
			case GFE_NOFLOOR:
				Log(LT_Info,"GF:			%d %d NOFLOOR: %d", *(seq+i), *(seq+i+1), *(seq+i+1) );
				i++;
				break;
			case GFE_STARTANIM:
				Log(LT_Info,"GF:			%d %d LARA START ANIM: %d", *(seq+i), *(seq+i+1), *(seq+i+1) );
				i++;
				break;
			case GFE_NUMSECRETS:
				Log(LT_Info,"GF:			%d %d NUMBER OF SECRETS: %d", *(seq+i), *(seq+i+1), *(seq+i+1) );
				i++;
				break;
			case GFE_ADD2INV:
				if ( *(seq+i+1)<1000 )
					Log(LT_Info,"GF:			%d %d ADD TO INVENTORY: %s", *(seq+i), *(seq+i+1), GF_inv_types[*(seq+i+1)] );
				else
					Log(LT_Info,"GF:			%d %d START INVENTORY: %s", *(seq+i), *(seq+i+1), GF_inv_types[*(seq+i+1)-1000] );
				i++;
				break;
			case GFE_REMOVE_WEAPONS:
				Log(LT_Info,"GF:			%d REMOVE WEAPONS", *(seq+i) );
				break;
			case GFE_REMOVE_AMMO:
				Log(LT_Info,"GF:			%d REMOVE AMMO", *(seq+i) );
				break;
			case GFE_KILL2COMPLETE:
				Log(LT_Info,"GF:			%d KILL TO COMPLETE", *(seq+i) );
				break;
			default:
				Log(LT_Info,"GF:			ERROR: Unknown sequence command (%d)", *(seq+i));
				return;
		}
	}
}
#endif
#endif

//#define CHECK_ALIGNS

#define THIS_SCRIPT_VERSION 3
int S_LoadGameFlow( char *filename )
{
	DWORD read;
	HANDLE file;
	sint16 number,size;
//	sint16 *seqptr;
//	sint16 totalsize;
	int	i;

#ifdef CD_LOAD
  	filename = GetFullPath( filename );
#endif
#ifdef GAMEDEBUG
	Log(LT_Info,"GF:	Loading GameFlow Data");
#endif
	// WIN95: open file
	file = CreateFile(filename, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
	if (file == INVALID_HANDLE_VALUE)
	{
		Log(LT_Info,"GF:	ERROR: Could not open file '%s'",filename);
		return (0);
	}

	// Read Script Version number
	MyReadFile(file, &GF_ScriptVersion, sizeof(uint32), &read, NULL);
	if (GF_ScriptVersion!=THIS_SCRIPT_VERSION)
	{
		Log(LT_Info,"GF:	ERROR: Incorrect script version %d (%d)",GF_ScriptVersion,THIS_SCRIPT_VERSION);
		return(0);
	}

	MyReadFile(file, GF_Description, 256, &read, NULL);

	Log(LT_Info,GF_Description);

	// Read in GAMEFLOW_INFO
	MyReadFile(file, &size, sizeof(sint16), &read, NULL );
	if (size!=sizeof(GAMEFLOW_INFO))
	{
	 	Log(LT_Info,"GF:	ERROR: Incorrect GAMEFLOW_INFO size %d (%d)",size,sizeof(GAMEFLOW_INFO));
		return(0);
	}
	MyReadFile(file, &gameflow, sizeof(GAMEFLOW_INFO), &read, NULL);
#ifdef GAMEDEBUG
	Log(LT_Info,"GF:	Cyphered Strings:%d", gameflow.cyphered_strings);
	Log(LT_Info,"GF:	Cypher Code:%x", gameflow.cypher_code);
	
	Log(LT_Info,"GF:	%d level(s)", gameflow.num_levels);
	Log(LT_Info,"GF:	%d pic file(s)", gameflow.num_picfiles);
	Log(LT_Info,"GF:	%d title file(s)", gameflow.num_titlefiles);
	Log(LT_Info,"GF:	%d fmv file(s)", gameflow.num_fmvfiles);
	Log(LT_Info,"GF:	%d cut file(s)", gameflow.num_cutfiles);
	Log(LT_Info,"GF:	%d demo(s)", gameflow.num_demos);
	Log(LT_Info,"GF:	Title Track: %d", gameflow.title_track);
	Log(LT_Info,"GF:	Single Playable: %d", gameflow.singlelevel);

	Log(LT_Info,"GF:	Title Enabled? %d", gameflow.title_disabled);
	Log(LT_Info,"GF:	Title Replace: %x", gameflow.title_replace);
	Log(LT_Info,"GF:	First Option: %x", gameflow.firstOption);
	Log(LT_Info,"GF:	OnDeath in demoplay: %x", gameflow.ondeath_demo_mode);
	Log(LT_Info,"GF:	OnDeath in gameplay: %x", gameflow.ondeath_ingame);
	Log(LT_Info,"GF:	OnDemo User Interrupt: %x", gameflow.on_demo_interrupt);
	Log(LT_Info,"GF:	OnDemo End: %x", gameflow.on_demo_end);
	Log(LT_Info,"GF:	NoInput time: %d seconds", gameflow.noinput_time/30);
	Log(LT_Info,"GF:	Noinput TimeOut? %d", gameflow.noinput_timeout);

	Log(LT_Info,"GF:	CheatmodeCheck Disabled? %d", gameflow.cheatmodecheck_disabled);
	Log(LT_Info,"GF:	Load/Save Disabled? %d", gameflow.loadsave_disabled);
	Log(LT_Info,"GF:	ScreenSizing Disabled? %d", gameflow.screensizing_disabled);
	Log(LT_Info,"GF:	LockOut OptionRing? %d", gameflow.lockout_optionring);
	Log(LT_Info,"GF:	'Dozy' Cheat? %d", gameflow.dozy_cheat_enabled);
#endif

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned LevelNames");return(0);}
#endif

	// Level Name/Filename Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading LevelNames");
#endif
	GF_Level_Names = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Level_Names)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Level_Names(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Level_Names, &GF_levelnames_buffer, &read, file ))
		return(0);

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned PicFileNames");return(0);}
#endif

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Pic FNames");
#endif
	GF_picfilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_picfiles*sizeof(char*));
	if (!GF_picfilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_picfilenames(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_picfiles, GF_picfilenames,  &GF_picfilenames_buffer, &read, file ))
		return(0);

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned TitleFileNames");return(0);}
#endif

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Title FNames");
#endif
	GF_titlefilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_titlefiles*sizeof(char*));
	if (!GF_titlefilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_titlefilenames(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_titlefiles, GF_titlefilenames, &GF_titlefilenames_buffer, &read, file ))
		return(0);

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned FMVFileNames");return(0);}
#endif

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading FMV FNames");
#endif
	GF_fmvfilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_fmvfiles*sizeof(char*));
	if (!GF_fmvfilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_fmvfileenames (%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_fmvfiles, GF_fmvfilenames, &GF_fmvfilenames_buffer, &read, file ))
		return(0);

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned LevelFileNames");return(0);}
#endif

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Level FNames");
#endif
	GF_levelfilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_levelfilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_levelfilenames(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_levelfilenames, &GF_levelfilenames_buffer, &read, file ))
		return(0);

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned CutsceneFileNames");return(0);}
#endif

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Cut FNames");
#endif
	GF_cutscenefilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_cutfiles*sizeof(char*));
	if (!GF_cutscenefilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_cutscenefilenames(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_cutfiles, GF_cutscenefilenames, &GF_cutscenefilenames_buffer, &read, file ))
		return(0);

//#ifdef CHECK_ALIGNS
//	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
//	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned DemoFileNames");return(0);}
//#endif

/*
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Demo FNames");
#endif
	GF_demofilenames = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_demos*sizeof(char*));
	if (!GF_demofilenames)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_demofilenames(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_demos, GF_demofilenames, &GF_demofilenames_buffer, &read, file ))
		return(0);
*/

/********************  SEQUENCE DATA  *********************************/

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned Sequence Offsets");return(0);}
#endif

	// Read-in offsets into seq buffer for each sequence
	MyReadFile(file, &LGF_offsets, sizeof(sint16)*(gameflow.num_levels+1), &read, NULL );
	MyReadFile(file, &size, sizeof(sint16), &read, NULL );

#if CODER==JASON
	Log(LT_Info,"GF:	Sequence Data= %d bytes",size);
#endif

#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned Sequence Data");return(0);}
#endif

	// Read-in sequence data
	GF_sequence_buffer = (sint16*)GlobalAlloc(GMEM_FIXED, size);
	if (!GF_sequence_buffer)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for sequences (%d bytes)", size);
		return (0); 
	}
	MyReadFile(file, GF_sequence_buffer, size, &read, NULL );

	// Frontend Sequence
	GF_frontendSequence = GF_sequence_buffer;
#if CODER==JASON
	Log(LT_Info,"GF:		FrontEnd Sequence");
	log_seq(GF_frontendSequence);
#endif
	for (i=0;i<gameflow.num_levels;i++)
	{
		GF_level_sequence_list[i] = GF_sequence_buffer+LGF_offsets[i+1]/2;
#if CODER==JASON
		Log(LT_Info,"GF:		Level %d Sequence",i);
		log_seq( GF_level_sequence_list[i] );
#endif
	}
/*	for (i=0;i<gameflow.num_demos;i++)
	{
		GF_demo_sequence_list[i] = GF_sequence_buffer+LGF_offsets[gameflow.num_levels+i+1]/2;
#if CODER==JASON
		Log(LT_Info,"GF:		Demo %d Sequence",i);
		log_seq( GF_demo_sequence_list[i] );
#endif
	}
*/
#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned Valid_demos List");return(0);}
#endif

	// Read level number that are actually demos
	if (gameflow.num_demos)
		MyReadFile(file, GF_valid_demos, sizeof(sint16)*gameflow.num_demos, &read, NULL );
					 
	
/********************  GAME STRINGS  *********************************/
#ifdef CHECK_ALIGNS
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=0x7777 ){Log(LT_Info,"GF:	ERROR: Mis-aligned String Data");return(0);}
#endif

	// GameText Strings
	MyReadFile(file, &number, sizeof(sint16), &read, NULL );
	if ( number!=GT_NUM_GAMESTRINGS )
	{
		Log(LT_Info,"GF:	ERROR: Incorrect number of GameStrings ");
		Log(LT_Info,"GF:	Script=%d	Game=%d", number, GT_NUM_GAMESTRINGS);
		return(0);
	}

#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Game Strings");
#endif
	GF_GameStrings = (char**)GlobalAlloc(GMEM_FIXED, GT_NUM_GAMESTRINGS*sizeof(char*));
	if (!GF_GameStrings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_GameStrings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( GT_NUM_GAMESTRINGS, GF_GameStrings, &GF_GameStrings_buffer, &read, file ))
		return(0);
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading PC Strings");
#endif
	GF_PCStrings = (char**)GlobalAlloc(GMEM_FIXED, PCSTR_NUM_STRINGS*sizeof(char*));
	if (!GF_PCStrings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_PCStrings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( PCSTR_NUM_STRINGS, GF_PCStrings, &GF_PCStrings_buffer, &read, file ))
		return(0);


	// Puzzle1 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Puzzle1 Strings");
#endif
	GF_Puzzle1Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Puzzle1Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Puzzle1Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Puzzle1Strings, &GF_Puzzle1Strings_buffer, &read, file ))
		return(0);

	// Puzzle2 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Puzzle2 Strings");
#endif
	GF_Puzzle2Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Puzzle2Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Puzzle2Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Puzzle2Strings, &GF_Puzzle2Strings_buffer, &read, file ))
		return(0);

	// Puzzle3 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Puzzle3 Strings");
#endif
	GF_Puzzle3Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Puzzle3Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Puzzle3Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Puzzle3Strings, &GF_Puzzle3Strings_buffer, &read, file ))
		return(0);

	// Puzzle4 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Puzzle4 Strings");
#endif
	GF_Puzzle4Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Puzzle4Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Puzzle4Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Puzzle4Strings, &GF_Puzzle4Strings_buffer, &read, file ))
		return(0);
/*
	// Secret1 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Secret1 Strings");
#endif
	GF_Secret1Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Secret1Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Secret1Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Secret1Strings, &GF_Secret1Strings_buffer, &read, file ))
		return(0);

	// Secret2 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Secret2 Strings");
#endif
	GF_Secret2Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Secret2Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Secret2Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Secret2Strings, &GF_Secret2Strings_buffer, &read, file ))
		return(0);

	// Secret3 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Secret3 Strings");
#endif
	GF_Secret3Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Secret3Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Secret3Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Secret3Strings, &GF_Secret3Strings_buffer, &read, file ))
		return(0);

	// Secret4 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Secret4 Strings");
#endif
	GF_Secret4Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Secret4Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Secret4Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Secret4Strings, &GF_Secret4Strings_buffer, &read, file ))
		return(0);

	// Special1 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Special1 Strings");
#endif
	GF_Special1Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Special1Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Special1Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Special1Strings, &GF_Special1Strings_buffer, &read, file ))
		return(0);

	// Special2 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Special2 Strings");
#endif
	GF_Special2Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Special2Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Special2Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Special2Strings, &GF_Special2Strings_buffer, &read, file ))
		return(0);
*/
	// Pickup1 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Pickup1 Strings");
#endif
	GF_Pickup1Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Pickup1Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Pickup1Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Pickup1Strings, &GF_Pickup1Strings_buffer, &read, file ))
		return(0);

	// Pickup2 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Pickup2 Strings");
#endif
	GF_Pickup2Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Pickup2Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Pickup2Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Pickup2Strings, &GF_Pickup2Strings_buffer, &read, file ))
		return(0);


	// Key1 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Key1 Strings");
#endif
	GF_Key1Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Key1Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Key1Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Key1Strings, &GF_Key1Strings_buffer, &read, file ))
		return(0);

	// Key2 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Key2 Strings");
#endif
	GF_Key2Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Key2Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Key2Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Key2Strings, &GF_Key2Strings_buffer, &read, file ))
		return(0);

	// Key3 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Key3 Strings");
#endif
	GF_Key3Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Key3Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Key3Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Key3Strings, &GF_Key3Strings_buffer, &read, file ))
		return(0);

	// Key4 Strings
#if CODER==JASON
 	Log(LT_Info,"GF:	Loading Key4 Strings");
#endif
	GF_Key4Strings = (char**)GlobalAlloc(GMEM_FIXED, gameflow.num_levels*sizeof(char*));
	if (!GF_Key4Strings)
	{
 		Log(LT_Info,"GF:	Could not allocate memory for GF_Key4Strings ptrs(%d bytes)", size);
		return (0); 
	}
	if (!Read_Strings( gameflow.num_levels, GF_Key4Strings, &GF_Key4Strings_buffer, &read, file ))
		return(0);

	CloseHandle(file);
	return (1);
}


