
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <math.h>

#include "file.hpp"
#include "dat.hpp"
#include "../../zone/map.h"
//#define DEBUGDAT

#define VARSTRUCT_DECODE_TYPE(Type, Buffer) *(Type *)Buffer; Buffer += sizeof(Type);
#define VARSTRUCT_DECODE_STRING(String, Buffer) strcpy(String, Buffer); Buffer += strlen(String)+1;
#define VARSTRUCT_ENCODE_STRING(Buffer, String) sprintf(Buffer, String); Buffer += strlen(String) + 1;
#define VARSTRUCT_ENCODE_INTSTRING(Buffer, Number) sprintf(Buffer, "%i", Number); Buffer += strlen(Buffer) + 1;
#define VARSTRUCT_ENCODE_TYPE(Type, Buffer, Value) *(Type *)Buffer = Value; Buffer += sizeof(Type);
#define VARSTRUCT_SKIP_TYPE(Type, Buffer) Buffer += sizeof(Type);


float HeightWithinQuad(VERTEX p1, VERTEX p2, VERTEX p3, VERTEX p4, float x, float y)
{
	// This function returns the height of the given x/y point with the quad defined by p1,p2,p3 and p4.
	//
	// It is assumed that the point does in fact lie somewhere within the quad. If for some reason it doesn't,
	// the program will abort, as thre is a logic flaw.
	//
	/*
	printf("Quad:	%8.3f, %8.3f, %8.3f\n", p1.x, p1.y, p1.z);
	printf("	%8.3f, %8.3f, %8.3f\n", p2.x, p2.y, p2.z);
	printf("	%8.3f, %8.3f, %8.3f\n", p3.x, p3.y, p3.z);
	printf("	%8.3f, %8.3f, %8.3f\n", p4.x, p4.y, p4.z);
	printf("\n\nPoint:	%8.3f, %8.3f\n", x, y);
	*/

	FACE f;

	int inTriangle = 0;

	float fAB = (y-p1.y) * (p2.x-p1.x) - (x-p1.x) * (p2.y-p1.y);
	float fBC = (y-p2.y) * (p3.x-p2.x) - (x-p2.x) * (p3.y-p2.y);
	float fCA = (y-p3.y) * (p1.x-p3.x) - (x-p3.x) * (p1.y-p3.y);

	if((fAB * fBC >= 0) && (fBC * fCA >= 0))
	{
		inTriangle = 1;
		f.a = p1;
		f.b = p2;
		f.c = p3;
	}

	fAB = (y-p1.y) * (p3.x-p1.x) - (x-p1.x) * (p3.y-p1.y);
	fBC = (y-p3.y) * (p4.x-p3.x) - (x-p3.x) * (p4.y-p3.y);
	fCA = (y-p4.y) * (p1.x-p4.x) - (x-p4.x) * (p1.y-p4.y);


	if((fAB * fBC >= 0) && (fBC * fCA >= 0))
	{
		inTriangle = 2;
		f.a = p1;
		f.b = p3;
		f.c = p4;
	}

	if(inTriangle == 0)
	{
		printf("Something went wrong. Aborting in HeightWithinQuad\n");
		exit(0);
	}

	f.nx = (f.b.y - f.a.y)*(f.c.z - f.a.z) - (f.b.z - f.a.z)*(f.c.y - f.a.y);
	f.ny = (f.b.z - f.a.z)*(f.c.x - f.a.x) - (f.b.x - f.a.x)*(f.c.z - f.a.z);
	f.nz = (f.b.x - f.a.x)*(f.c.y - f.a.y) - (f.b.y - f.a.y)*(f.c.x - f.a.x);

	float len = sqrt(f.nx*f.nx + f.ny*f.ny + f.nz*f.nz);

	f.nx /= len;
	f.ny /= len;
	f.nz /= len;

	return (((f.nx) * (x - f.a.x) + (f.ny) * (y - f.a.y)) / -f.nz) + f.a.z;
}


string GetToken(uchar *&Buffer, int &Position)
{
	// This is used to return each Token, keyword, etc. from an EQG v4 .zon or .tog file
	//
	string Token;

	while((Buffer[Position] == 9) || (Buffer[Position] == 32))
		++Position;

	while((Buffer[Position] != 10) && (Buffer[Position] != 9) && (Buffer[Position] != 32))
	{
		if(Buffer[Position] > 32)
			Token = Token + (char)Buffer[Position];

		++Position;
	}
	++Position;
	return Token;
}

int AddModelName(Content_3D *C3D, string ModelName)
{
	vector<string>::iterator Iterator;

	for(unsigned int i = 0; i < C3D->ModelNames.size(); ++i)
	{
#ifndef WIN32
		if(!strcasecmp(C3D->ModelNames[i].c_str(), ModelName.c_str()))
#else
	   	if(!_stricmp(C3D->ModelNames[i].c_str(), ModelName.c_str()))
#endif
			return i;
	}



	C3D->ModelNames.push_back(ModelName);

	return C3D->ModelNames.size() - 1;
}

DATLoader::DATLoader()
{
	this->buffer = nullptr;
	this->buf_len = -1;
	this->archive = nullptr;
	this->status = 0;
}

DATLoader::~DATLoader()
{
	this->Close();
}

int DATLoader::Open(char *base_path, char *zone_name, Archive *archive) {

#ifdef DEBUGDAT
	printf("DATLoader::Open %s, [%s]\n", base_path, zone_name);
	fflush(stdout);
#endif
	unsigned int i, j, mat_count = 0;

	Zone_Model *zm;

	char *buffer, *buf_start;
	int buf_len, bone_count;

	char *filename;

	if(zone_name[strlen(zone_name) - 4] == '.')
		filename = zone_name;
	else
	{
		filename = new char[strlen(zone_name) + 5];
		sprintf(filename, "%s.ter", zone_name);
	}

	if(!GetFile((uchar **)&buffer, &buf_len, base_path, filename, archive))
	{
		if(filename != zone_name)
	    		delete[] filename;
		return 0;
	}
#ifdef DEBUGDAT
	printf("Filename = %s, zonename = %s\n", filename, zone_name);
#endif
	if(filename != zone_name)
		delete[] filename;


	buf_start = buffer;

	//TODO: Find out what these three unknowns are
	int Unknown1 = VARSTRUCT_DECODE_TYPE(uint32, buffer);
	int Unknown2 = VARSTRUCT_DECODE_TYPE(uint32, buffer);
	int Unknown3 = VARSTRUCT_DECODE_TYPE(uint32, buffer);

#ifdef DEBUGDAT
	printf("Unknowns: %i, %i, %i\n", Unknown1, Unknown2, Unknown3);
#endif

	char s[255];

	VARSTRUCT_DECODE_STRING(s, buffer)

#ifdef DEBUGDAT
	printf("('%s',)\n", s);
#endif

	int TileCount = VARSTRUCT_DECODE_TYPE(uint32, buffer);

#ifdef DEBUGDAT
	printf("TileCount is  %i\n", TileCount);
#endif

	// Most of these default values will be overwritten when we read the .zon file
	//
	int QuadsPerTile = 32;

	float MinLAT = -24;
	float MaxLAT = -1;
	float MinLNG = 0;
	float MaxLNG = 62;

	float UnitsPerVert = 5.0;

	float ZoneMinX = 0;
	float ZoneMaxX = 0;

	float ZoneMinY = 0;
	float ZoneMaxY = 0;

	float MinExtentX = -999999, MinExtentY = -999999, MinExtentZ = -999999;
	float MaxExtentX = 999999, MaxExtentY = 999999, MaxExtentZ = 999999;

	// Parse .zon file.
	//
	uchar *ZonBuffer;

	int ZonBufferLength, ZonPosition;

	if(!GetFile((uchar **)&ZonBuffer, &ZonBufferLength, base_path, "*.zon", archive))
	{
		printf("Unable to open .zon\n");
		exit(0);
	}
	else
	{
		ZonPosition = 0;

		while(ZonPosition < ZonBufferLength)
		{
			string Token = GetToken(ZonBuffer, ZonPosition);

			if(Token == "*MINLNG")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MinLNG = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MinLNG to %8.3f\n", MinLNG);
#endif
			}
			if(Token == "*MAXLNG")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MaxLNG = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MaxLNG to %8.3f\n", MaxLNG);
#endif
			}
			if(Token == "*MINLAT")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MinLAT = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MinLAT to %8.3f\n", MinLAT);
#endif
			}
			if(Token == "*MAXLAT")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MaxLAT = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MaxLAT to %8.3f\n", MaxLAT);
#endif
			}
			if(Token == "*UNITSPERVERT")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				UnitsPerVert = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set UnitsPerVert to %8.3f\n", UnitsPerVert);
#endif
			}
			if(Token == "*QUADSPERTILE")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				QuadsPerTile = Strings::ToInt(Token.c_str());
#ifdef DEBUGDAT
				printf("Set QuadsPerTile to %i\n", QuadsPerTile);
#endif
			}
			if(Token == "*MIN_EXTENTS")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MinExtentX = atof(Token.c_str());
				Token = GetToken(ZonBuffer, ZonPosition);
				MinExtentY = atof(Token.c_str());
				Token = GetToken(ZonBuffer, ZonPosition);
				MinExtentZ = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MinExtents to %8.3f, %8.3f, %8.3f\n", MinExtentX, MinExtentY, MinExtentZ);
#endif
			}
			if(Token == "*MAX_EXTENTS")
			{
				Token = GetToken(ZonBuffer, ZonPosition);
				MaxExtentX = atof(Token.c_str());
				Token = GetToken(ZonBuffer, ZonPosition);
				MaxExtentY = atof(Token.c_str());
				Token = GetToken(ZonBuffer, ZonPosition);
				MaxExtentZ = atof(Token.c_str());
#ifdef DEBUGDAT
				printf("Set MaxExtents to %8.3f, %8.3f, %8.3f\n", MaxExtentX, MaxExtentY, MaxExtentZ);
#endif
			}
		}
	}

	float UnitsPerVertX = UnitsPerVert;
	float UnitsPerVertY = UnitsPerVert;

	float LATRange = (MaxLAT - MinLAT);
	float LNGRange = (MaxLNG - MinLNG);

	ZoneMinX = MinLAT * QuadsPerTile * UnitsPerVert;
	ZoneMaxX = (MaxLAT + 1) * QuadsPerTile * UnitsPerVert;

	ZoneMinY = MinLNG * QuadsPerTile * UnitsPerVert;
	ZoneMaxY = (MaxLNG + 1) * QuadsPerTile * UnitsPerVert;

	int QuadCount = (QuadsPerTile * QuadsPerTile);
	int VertCount = ((QuadsPerTile + 1) * (QuadsPerTile + 1));
#ifdef DEBUGDAT
	printf("X Range: %8.3f to %8.3f\n", ZoneMinX, ZoneMaxX);
	printf("Y Range: %8.3f to %8.3f\n", ZoneMinY, ZoneMaxY);


	printf("UnitsPerVertX = %8.3f, UnitsPerVertY = %8.3f\n", UnitsPerVertX, UnitsPerVertY);

	printf("LATRange is from %8.3f to %8.3f (%8.3f), x = %8.3f to %8.3f (%8.3f)\n",
		MinLAT, MaxLAT, LATRange, ZoneMinX, ZoneMaxX, ZoneMaxX - ZoneMinX);

#endif
	int VertexNumber = -1;
	int PolyNumber = -1;

	this->model_data.zone_model = new Zone_Model;
	zm = this->model_data.zone_model;

	zm->vert_count = QuadCount * TileCount * 4;
	zm->poly_count = QuadCount * 2 * TileCount;

	zm->verts = new Vertex *[zm->vert_count];
	zm->polys = new Polygon *[zm->poly_count];

	this->model_data.plac_count = 0;
	this->model_data.model_count = 0;
	this->model_data.ObjectGroupCount = 0;


	float *Floats = new float[VertCount];
	int *Color1 = new int[VertCount];
	int *Color2 = new int[VertCount];
	uint8 *Bytes = new uint8[QuadCount];

	for(int TileNumber = 0; TileNumber < TileCount; ++TileNumber)
	{

		int TileLNG = VARSTRUCT_DECODE_TYPE(uint32, buffer);
		int TileLAT = VARSTRUCT_DECODE_TYPE(uint32, buffer);
		int TileUNK = VARSTRUCT_DECODE_TYPE(uint32, buffer);

		float TileYStart = ZoneMinY + (TileLNG - 100000 - MinLNG) * UnitsPerVertY * QuadsPerTile;
		float TileXStart = ZoneMinX + (TileLAT - 100000 - MinLAT) * UnitsPerVertX * QuadsPerTile;

#ifdef DEBUGDAT
		printf("TileXStart = %8.3f + %f * %f * %i\n", ZoneMinX, (TileLAT - 100000 - MinLAT) , UnitsPerVertX, QuadsPerTile);


		printf("****** Tile # %5d header %6d, %6d, %6d (LNG: %6d, LAT: %6d)\n", TileNumber, TileLNG, TileLAT, TileUNK,
			TileLNG - 100000, TileLAT - 100000);

		printf("TileXStart = %8.3f, TileYStart = %8.3f\n", TileXStart, TileYStart);
		printf("TileXEnd = %8.3f, TileYEnd = %8.3f\n", TileXStart + QuadsPerTile * UnitsPerVertX,
			TileYStart + QuadsPerTile * UnitsPerVertY);
#endif

		// Height Values
		//
		bool AllFloatsTheSame = true;

		for(int i = 0; i < VertCount; ++i)
		{
			Floats[i] = VARSTRUCT_DECODE_TYPE(float, buffer);
			if((i > 0) && (Floats[i] != Floats[0]))
				AllFloatsTheSame = false;
		}


		// It is an assumption that these next two groups are Color info, but they look like it.
		//
		for(int i = 0; i < VertCount; ++i)
		{
			Color1[i] = VARSTRUCT_DECODE_TYPE(uint32, buffer);
		}

		for(int i = 0; i < VertCount; ++i)
		{
			Color2[i] = VARSTRUCT_DECODE_TYPE(uint32, buffer);
		}

		// The low order bit of this group of bytes indicates whether the corresponding quad should
		// be rendered or not.
		for(int i = 0; i < QuadCount; ++i)
		{
			Bytes[i] = VARSTRUCT_DECODE_TYPE(uint8, buffer);
		}


		float UnkFloat = VARSTRUCT_DECODE_TYPE(float, buffer);

		int UnkUnk = VARSTRUCT_DECODE_TYPE(uint32, buffer);

		buffer -= 4;

		float UnkUnk2 = VARSTRUCT_DECODE_TYPE(float, buffer);
#ifdef DEBUGDAT
		printf("Unknowns  %.1f %i (%8.3f)\n", UnkFloat, UnkUnk, UnkUnk2);
#endif

		if(UnkUnk > 0)
		{
			int8 UnkByte = VARSTRUCT_DECODE_TYPE(uint8, buffer);
#ifdef DEBUGDAT
			printf("Byte is  (%i,)\n", UnkByte);
#endif
			if(UnkByte > 0)
			{
				float f1 = VARSTRUCT_DECODE_TYPE(float, buffer);
				float f2 = VARSTRUCT_DECODE_TYPE(float, buffer);
				float f3 = VARSTRUCT_DECODE_TYPE(float, buffer);
				float f4 = VARSTRUCT_DECODE_TYPE(float, buffer);
#ifdef DEBUGDAT
				printf("Floats:  %.1f %.1f %1.f %.1f\n", f1, f2, f3, f4);
#endif
			}

			float f1 = VARSTRUCT_DECODE_TYPE(float, buffer);
#ifdef DEBUGDAT
			printf("Float:  (%.1f,)\n", f1);
#endif
		}

		int LayerCount = VARSTRUCT_DECODE_TYPE(uint32, buffer);

		VARSTRUCT_DECODE_STRING(s, buffer);

		int OverlayCount = 0;

		for(int Layer = 1; Layer < LayerCount; ++Layer)
		{
			VARSTRUCT_DECODE_STRING(s, buffer);

			int Size = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			for(int b = 0; b < (Size * Size); ++b)
			{
				uint8 Byte = VARSTRUCT_DECODE_TYPE(uint8, buffer);
			}

			++OverlayCount;

		}

		int Count1 = VARSTRUCT_DECODE_TYPE(uint32, buffer);
#ifdef DEBUGDAT
		printf("Count 1 is  (%iL,)\n", Count1);
#endif

		// The Count1 section is a list of single placeable models
		//
		for(int j = 0; j < Count1; ++j)
		{
			char ModelName[255];

			VARSTRUCT_DECODE_STRING(ModelName, buffer);
#ifdef DEBUGDAT
			printf("%s\n", ModelName);
#endif
			int ModelNumber = AddModelName(&model_data, ModelName);

			VARSTRUCT_DECODE_STRING(s, buffer);
#ifdef DEBUGDAT
			printf("%s\n", s);
#endif

			// Although the LNG/LAT is included for each model within this tile, the values always appear to be
			// the same as for the parent tile.
			//
			int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			float x = VARSTRUCT_DECODE_TYPE(float, buffer);
			float y = VARSTRUCT_DECODE_TYPE(float, buffer);
			float z = VARSTRUCT_DECODE_TYPE(float, buffer);

			float RotX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			VARSTRUCT_DECODE_TYPE(uint8, buffer);

			Placeable p(ModelNumber, 0, 0, 0, RotX, RotY, RotZ, ScaleX, ScaleY, ScaleZ);

			model_data.PlaceableList.push_back(p);
#ifdef DEBUGDAT
			printf("Model: %s LOC: %8.3f, %8.3f, %8.3f, ROT: %8.3f, %8.3f, %8.3f\n", ModelName, x, y, z, RotX, RotY, RotZ);
#endif
			ObjectGroupEntry NewObjectGroup;

			NewObjectGroup.FromTOG = false;

			NewObjectGroup.y = y;
			NewObjectGroup.x = x;
			NewObjectGroup.z = z;

			NewObjectGroup.TileX = TileYStart;
			NewObjectGroup.TileY = TileXStart;

			// The objects in the Count1 section have a Z offset relative to the ground level, so we calculate the
			// ground level within this quad for the given x/y offsets. However ...
			//
			// Some objects within a tile have an X/Y offset specified such that they actually sit within the bounds
			// of another tile, which means based on the data for the tile we are processing, we should be unable to
			// calculate the Z value for that object.
			//
			// By hand editing an .EQG, it was discovered that what the client appears to do is calculate the Z value
			// for these objects by adjusting the x/y offsets by +/- (QuadsPerTile * UnitsPerVert) until the offsets
			// are within this tiles boundaries and using the corresponding height values.
			//
			// For example, if a model has an offset of -157.5, -157.5 and QPT * UPV = 160, then the height value used
			// would be the height at +2.5, +2.5 in this tile.
			//
			// This doesn't make a great deal of sense, but it is how the EQ graphics engine appears to do things.
			//
			// These 'Adjusted' x/y offsets are used only for determing the ground Z for the object. The actual placement
			// of the object relative to the terrain uses the unadjusted offsets.
			//
			// For the record, as of SoF, this applies to the following zones and objects:
			//
			//	arcstone: Model translations adjusted from  -14.000,  219.000 to  146.000,   59.000
			//	arcstone: Model translations adjusted from  159.692,  256.589 to  159.692,   96.589
			//	arcstone: Model translations adjusted from  122.314,  170.098 to  122.314,   10.098
			//	elddar: Model translations adjusted from  144.980,  396.406 to  144.980,   76.406
			//	elddar: Model translations adjusted from  167.534, -193.584 to    7.534,  126.416
			//	kithicor: Model translations adjusted from   18.217,  166.512 to   18.217,    6.512
			//	hro.out: Model translations adjusted from   38.249,   -4.658 to   38.249,  155.342
			//	steppes: Model translations adjusted from  101.380,  380.000 to  101.380,  124.000
			//	steppes: Model translations adjusted from   32.770, -210.000 to   32.770,   46.000
			//	steppes: Model translations adjusted from  560.000,  150.000 to   48.000,   22.000
			//	steppes: Model translations adjusted from  420.000,  445.000 to   36.000,   61.000
			//
			float TerrainHeight = 0;

			float AdjustedX = x;

			float AdjustedY = y;

			if(AdjustedX < 0)
				AdjustedX = AdjustedX + (-(int)(AdjustedX / (UnitsPerVertX * QuadsPerTile)) + 1) * (UnitsPerVertX * QuadsPerTile);
			else
				AdjustedX = fmod(AdjustedX, UnitsPerVertX * QuadsPerTile);

			if(AdjustedY < 0)
				AdjustedY = AdjustedY + (-(int)(AdjustedY / (UnitsPerVertX * QuadsPerTile)) + 1) * (UnitsPerVertX * QuadsPerTile);
			else
				AdjustedY = fmod(AdjustedY, UnitsPerVertY * QuadsPerTile);

#ifdef DEBUGDAT
			if((AdjustedX != x) || (AdjustedY != y))
				printf(" Model translations adjusted from %8.3f, %8.3f to %8.3f, %8.3f\n", x, y, AdjustedX, AdjustedY);
#endif

			//	Calculated which quad the adjusted x/y coordinated of the object lie in.
			//
			int RowNumber = (int)(AdjustedY / UnitsPerVertY);

			int Column = (int)(AdjustedX / UnitsPerVertX);

			int Quad = RowNumber * QuadsPerTile + Column;

			float QuadVertex1Z = Floats[Quad + RowNumber];
			float QuadVertex2Z = Floats[Quad + RowNumber + QuadsPerTile + 1];
			float QuadVertex3Z = Floats[Quad + RowNumber + QuadsPerTile + 2];
			float QuadVertex4Z = Floats[Quad + RowNumber + 1];

			VERTEX P1(RowNumber * UnitsPerVertX, (Quad % QuadsPerTile) * UnitsPerVertY, QuadVertex1Z);
			VERTEX P2(P1.x + UnitsPerVertX, P1.y, QuadVertex2Z);
			VERTEX P3(P1.x + UnitsPerVertX, P1.y + UnitsPerVertY, QuadVertex3Z);
			VERTEX P4(P1.x, P1.y + UnitsPerVertY, QuadVertex4Z);

			TerrainHeight = HeightWithinQuad(P1, P2, P3, P4, AdjustedY, AdjustedX);

#ifdef DEBUGDAT
			printf("XY: %8.3f, %8.3f Row: %i, Columm: %i, Quad: %i\n", x, y, RowNumber, Column, Quad);
			printf("Zs: %8.3f, %8.3f, %8.3f, %8.3f\n", QuadVertex1Z, QuadVertex2Z, QuadVertex3Z, QuadVertex4Z);
			printf("Height is %8.3f\n", TerrainHeight);
#endif
			NewObjectGroup.TileZ = TerrainHeight;

			NewObjectGroup.RotX = 0;
			NewObjectGroup.RotY = 0;
			NewObjectGroup.RotZ = 0;

			NewObjectGroup.ScaleX = 1;
			NewObjectGroup.ScaleY = 1;
			NewObjectGroup.ScaleZ = 1;

			NewObjectGroup.SubObjects.push_back(model_data.PlaceableList.size()-1);

			model_data.ObjectGroups.push_back(NewObjectGroup);
#ifdef DEBUGDAT
			printf("ObjectGroup %i created.\n", model_data.ObjectGroups.size()-1);
#endif

		}

		// We don't currently do anything with the Count2 objects.
		// Things like water, teleports, zonelines seem to be in here, judging from the strings.
		//
		int Count2 = VARSTRUCT_DECODE_TYPE(uint32, buffer);
#ifdef DEBUGDAT
		printf("Count 2 is  (%iL,)\n", Count2);
#endif
		for(int j = 0; j < Count2; ++j)
		{
			VARSTRUCT_DECODE_STRING(s, buffer);
#ifdef DEBUGDAT
			printf("%s\n", s);
#endif

			VARSTRUCT_DECODE_TYPE(uint32, buffer);

			VARSTRUCT_DECODE_STRING(s, buffer);
#ifdef DEBUGDAT
			printf("%s\n", s);
#endif

			int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);
			int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			float x = VARSTRUCT_DECODE_TYPE(float, buffer);
			float y = VARSTRUCT_DECODE_TYPE(float, buffer);
			float z = VARSTRUCT_DECODE_TYPE(float, buffer);

			float RotX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float SizeX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float SizeY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float SizeZ = VARSTRUCT_DECODE_TYPE(float, buffer);
		}

		int Count3 = VARSTRUCT_DECODE_TYPE(uint32, buffer);

		// We don't currently do anything with the Count 3 data. Mostly seems to be camp fires and suchlike.
		//
#ifdef DEBUGDAT
		printf("Count 3 is  (%iL,)\n", Count3);
#endif
		for(int j = 0; j < Count3; ++j)
		{
			VARSTRUCT_DECODE_STRING(s, buffer);
#ifdef DEBUGDAT
			printf("%s\n", s);
#endif
			VARSTRUCT_DECODE_STRING(s, buffer);
#ifdef DEBUGDAT
			printf("%s\n", s);
#endif

			VARSTRUCT_DECODE_TYPE(uint8, buffer);

			int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);
			int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			float x = VARSTRUCT_DECODE_TYPE(float, buffer);
			float y = VARSTRUCT_DECODE_TYPE(float, buffer);
			float z = VARSTRUCT_DECODE_TYPE(float, buffer);

			float RotX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			VARSTRUCT_DECODE_TYPE(float, buffer);
		}

		int Count4 = VARSTRUCT_DECODE_TYPE(uint32, buffer);

		// The data in this section mostly references .tog (Object Group) files.
		//
#ifdef DEBUGDAT
		printf("Count 4 is  (%iL,)\n", Count4);
#endif
		for(int j = 0; j < Count4; ++j)
		{
			char TogName[255];

			VARSTRUCT_DECODE_STRING(TogName, buffer);
#ifdef DEBUGDAT
			printf("%s\n", TogName);
#endif

			int Longitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);
			int Latitude = VARSTRUCT_DECODE_TYPE(uint32, buffer);

			float x = VARSTRUCT_DECODE_TYPE(float, buffer);
			float y = VARSTRUCT_DECODE_TYPE(float, buffer);
			float z = VARSTRUCT_DECODE_TYPE(float, buffer);

			float RotX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float RotZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float ScaleX = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleY = VARSTRUCT_DECODE_TYPE(float, buffer);
			float ScaleZ = VARSTRUCT_DECODE_TYPE(float, buffer);

			float ZAdjust = VARSTRUCT_DECODE_TYPE(float, buffer);
#ifdef DEBUGDAT
			printf("ZAdjust %8.3f\n", ZAdjust);
#endif
			char TogFileName[255];

			sprintf(TogFileName, "%s.tog", TogName);

			uchar *TogBuffer;
			int TogBufferLength, TogPosition;

			if(!GetFile((uchar **)&TogBuffer, &TogBufferLength, base_path, TogFileName, archive))
			{
				printf("Unable to open %s\n", TogFileName);
			}
			else
			{
				ObjectGroupEntry NewObjectGroup;

				NewObjectGroup.FromTOG = true;

				NewObjectGroup.y = y;
				NewObjectGroup.x = x;
				NewObjectGroup.z = z;

				NewObjectGroup.TileX = TileYStart;
				NewObjectGroup.TileY = TileXStart;

				NewObjectGroup.TileZ = 0;

				NewObjectGroup.RotX = RotX;
				NewObjectGroup.RotY = RotY;
				NewObjectGroup.RotZ = RotZ;

				NewObjectGroup.ScaleX = ScaleX;
				NewObjectGroup.ScaleY = ScaleY;
				NewObjectGroup.ScaleZ = ScaleZ;

				NewObjectGroup.z = NewObjectGroup.z + (NewObjectGroup.ScaleZ * ZAdjust);

				Placeable p;

				TogPosition = 0;

				string ModelName;

				while(TogPosition < TogBufferLength)
				{
					string Token = GetToken(TogBuffer, TogPosition);

					if(Token == "*NAME")
					{
						Token = GetToken(TogBuffer, TogPosition);
						ModelName = Token;
						p.model = AddModelName(&model_data, Token.c_str());
					}
					if(Token == "*POSITION")
					{
						p.x = atof(GetToken(TogBuffer, TogPosition).c_str());
						p.y = atof(GetToken(TogBuffer, TogPosition).c_str());
						p.z = atof(GetToken(TogBuffer, TogPosition).c_str());
					}
					if(Token == "*ROTATION")
					{
						p.rx = atof(GetToken(TogBuffer, TogPosition).c_str());
						p.ry = atof(GetToken(TogBuffer, TogPosition).c_str());
						p.rz = atof(GetToken(TogBuffer, TogPosition).c_str());
					}
					if(Token == "*SCALE")
					{
						p.scale[0] = atof(GetToken(TogBuffer, TogPosition).c_str());
						p.scale[1] = p.scale[2] = p.scale[0];
					}
					if(Token == "*END_OBJECT")
					{
#ifdef DEBUGDAT
						printf("Pushing back object with model: %3i %s\n", p.model, ModelName.c_str());
						printf(" Position  : %8.3f, %8.3f, %8.3f\n", p.x, p.y, p.z);
						printf(" Rotations : %8.3f, %8.3f, %8.3f\n", p.rx, p.ry, p.rz);
						printf(" Scale     : %8.3f, %8.3f, %8.3f\n", p.scale[0], p.scale[1], p.scale[2]);
#endif
						model_data.PlaceableList.push_back(p);

						NewObjectGroup.SubObjects.push_back(model_data.PlaceableList.size()-1);
					}
					if(Token == "*END_OBJECTGROUP")
					{
#ifdef DEBUGDAT
						printf("Pushing back new ObjectGroup\n");
						printf(" Position  : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.x, NewObjectGroup.y, NewObjectGroup.z);
						printf(" Rotations : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.RotX, NewObjectGroup.RotY, NewObjectGroup.RotZ);
						printf(" Scale     : %8.3f, %8.3f, %8.3f\n", NewObjectGroup.ScaleX, NewObjectGroup.ScaleY, NewObjectGroup.ScaleZ);
						printf("Adjusting NewObjectGroup.z by %8.3f\n", NewObjectGroup.ScaleZ * ZAdjust);
#endif
						model_data.ObjectGroups.push_back(NewObjectGroup);
#ifdef DEBUGDAT
						printf("ObjectGroup %i created.\n", model_data.ObjectGroups.size()-1);
						// There can be several object groups defined in the same .tog
						NewObjectGroup.SubObjects.clear();
#endif
					}
					if(Token == "*BEGIN_AREA")
					{
						while(Token != "*END_AREA")
						{
#ifdef DEBUGDAT
							printf("Skipping %s\n", Token.c_str());
#endif
							Token = GetToken(TogBuffer, TogPosition);
						}
					}


				}
			}

		}

		// If all the height values in this tile are the same, then we can just make one big quad of two triangles.
		//
		// This cuts over 200MB off the size of the oceanoftears .map, however not very much for other zones.
		//
		if(AllFloatsTheSame)
		{

			float QuadVertex1X = TileXStart;
			float QuadVertex1Y = TileYStart;
			float QuadVertex1Z = Floats[0];

			float QuadVertex2X = QuadVertex1X + (QuadsPerTile * UnitsPerVertX);
			float QuadVertex2Y = QuadVertex1Y;
			float QuadVertex2Z = QuadVertex1Z;

			float QuadVertex3X = QuadVertex2X;
			float QuadVertex3Y = QuadVertex1Y + (QuadsPerTile * UnitsPerVertY);
			float QuadVertex3Z = QuadVertex1Z;

			float QuadVertex4X = QuadVertex1X;
			float QuadVertex4Y = QuadVertex3Y;
			float QuadVertex4Z = QuadVertex1Z;


			++VertexNumber;

			zm->verts[VertexNumber++] = new Vertex(QuadVertex1Y, QuadVertex1X, QuadVertex1Z);

			zm->verts[VertexNumber++] = new Vertex(QuadVertex2Y, QuadVertex2X, QuadVertex2Z);

			zm->verts[VertexNumber++] = new Vertex(QuadVertex3Y, QuadVertex3X, QuadVertex3Z);

			zm->verts[VertexNumber] = new Vertex(QuadVertex4Y, QuadVertex4X, QuadVertex4Z);

			PolyNumber++;

			zm->polys[PolyNumber] = new Polygon;

			zm->polys[PolyNumber]->v1 = VertexNumber;
			zm->polys[PolyNumber]->v2 = VertexNumber - 2;
			zm->polys[PolyNumber]->v3 = VertexNumber - 1;
			zm->polys[PolyNumber]->tex = -1;

			PolyNumber++;

			zm->polys[PolyNumber] = new Polygon;

			zm->polys[PolyNumber]->v1 = VertexNumber;
			zm->polys[PolyNumber]->v2 = VertexNumber - 3;
			zm->polys[PolyNumber]->v3 = VertexNumber - 2;
			zm->polys[PolyNumber]->tex = -1;
		}
		else
		{
			int RowNumber = -1;

			// This is the code that generates triangles for this tile of the terrain based on the height values we decode above.
			//
			for(int Quad = 0; Quad < QuadCount; ++Quad)
			{
				if((Quad % QuadsPerTile) == 0)
					++RowNumber;

				// Other common values for this Byte are 0x80, 0x82. Setting them all to zero has no obvious visual
				// effect.
				if(Bytes[Quad] & 0x01) // Indicates Quad should not be included because an object will overlay it.
					continue;

				float QuadVertex1X = TileXStart + (RowNumber * UnitsPerVertX);
				float QuadVertex1Y = TileYStart + (Quad % QuadsPerTile) * UnitsPerVertY;
				float QuadVertex1Z = Floats[Quad + RowNumber];

				float QuadVertex2X = QuadVertex1X + UnitsPerVertX;
				float QuadVertex2Y = QuadVertex1Y;
				float QuadVertex2Z = Floats[Quad + RowNumber + QuadsPerTile + 1];

				float QuadVertex3X = QuadVertex1X + UnitsPerVertX;
				float QuadVertex3Y = QuadVertex1Y + UnitsPerVertY;
				float QuadVertex3Z = Floats[Quad + RowNumber + QuadsPerTile + 2];

				float QuadVertex4X = QuadVertex1X;
				float QuadVertex4Y = QuadVertex1Y + UnitsPerVertY;
				float QuadVertex4Z = Floats[Quad + RowNumber + 1];

				++VertexNumber;

				zm->verts[VertexNumber++] = new Vertex(QuadVertex1Y, QuadVertex1X, QuadVertex1Z);

				zm->verts[VertexNumber++] = new Vertex(QuadVertex2Y, QuadVertex2X, QuadVertex2Z);

				zm->verts[VertexNumber++] = new Vertex(QuadVertex3Y, QuadVertex3X, QuadVertex3Z);

				zm->verts[VertexNumber] = new Vertex(QuadVertex4Y, QuadVertex4X, QuadVertex4Z);

				PolyNumber++;

				zm->polys[PolyNumber] = new Polygon;

				zm->polys[PolyNumber]->v1 = VertexNumber;
				zm->polys[PolyNumber]->v2 = VertexNumber - 2;
				zm->polys[PolyNumber]->v3 = VertexNumber - 1;
				zm->polys[PolyNumber]->tex = -1;

				PolyNumber++;

				zm->polys[PolyNumber] = new Polygon;

				zm->polys[PolyNumber]->v1 = VertexNumber;
				zm->polys[PolyNumber]->v2 = VertexNumber - 3;
				zm->polys[PolyNumber]->v3 = VertexNumber - 2;
				zm->polys[PolyNumber]->tex = -1;

			}
		}
		zm->vert_count = VertexNumber + 1;
		zm->poly_count = PolyNumber + 1;

	}

	delete [] Floats;
	delete [] Color1;
	delete [] Color2;
	delete [] Bytes;

	this->status = 1;
	return 1;
}

int DATLoader::Close() {

	if(!status)
		return 1;

	Zone_Model *zm = this->model_data.zone_model;

	int i;

	for(i = 0; i < zm->vert_count; ++i)
		delete zm->verts[i];

	for(i = 0; i < zm->poly_count; ++i)
		delete zm->polys[i];

	delete[] zm->verts;

	delete[] zm->polys;

	delete this->model_data.zone_model;

	status = 0;

	return 1;
}
