#include "doom.h"
#include <io.h>
#include <string.h>
#include <stdio.h>

#define MAXREAD		32768L


DoomVertexObject *		VertexPointer = NULL;
DoomLineDefObject *		LineDefPointer = NULL;
DoomThingObject *		ThingPointer = NULL;
DoomSideDefObject *		SideDefPointer = NULL;
DoomSectorObject *		SectorPointer = NULL;


// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                             lread/lwrite                               º
// º                                                                        º
// º Read and write large blocks of data to and from the disk.              º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DWORD lread (int FileHandle,VOID FAR * pv,DWORD ul)
{
	DWORD			ulT = ul;
	char huge *		hp = (char *) pv;

	// This procedure allows reading in of huge amounts of data from a file.
	while (ul > (DWORD) MAXREAD)
	{
		if (_lread (FileHandle,(LPSTR) hp,(WORD) MAXREAD) != MAXREAD)
			return 0;
		ul -= MAXREAD;
		hp += MAXREAD;
	}
	
	if (_lread (FileHandle,(LPSTR) hp,(WORD) ul) != (WORD) ul)
		return 0;
		
    return ulT;
}


DWORD lwrite (int FileHandle,VOID FAR * pv,DWORD ul)
{
    DWORD     ulT = ul;
    char huge *hp = (char *) pv;

	// This procedure allows writing out of huge amounts of data from a file.
    while (ul > MAXREAD)
    {
        if (_lwrite (FileHandle,(LPSTR) hp,(WORD) MAXREAD) != MAXREAD)
			return 0;
        ul -= MAXREAD;
        hp += MAXREAD;
    }

    if (_lwrite (FileHandle,(LPSTR) hp,(WORD) ul) != (WORD) ul)
        return 0;

    return ulT;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                             Base Class For Doom                        º
// º                                                                        º
// º Using this base class allows us to read data from the file we do not   º
// º specifically understand. We can subclass from here to handle data      º
// º we are particularly interested in e.g. vertexes, linedefs.             º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomObject::DoomObject ()
{
	// Zeroing our fields.
	NoOfObjects = 0;
	NoOfSpareObjects = 200;
	MemHandle = NULL;
	MemPointer = NULL;
}


DoomObject::~DoomObject ()
{
	GlobalUnlock (MemHandle);
	GlobalFree (MemHandle);
}


DoomObject::DoomObject (int FileHandle,DirectoryEntry * Entry)
{
	// Zeroing our fields.
	NoOfObjects = 0;
	NoOfSpareObjects = 0;
	MemHandle = NULL;
	MemPointer = NULL;

	MemHandle = GlobalAlloc (GMEM_MOVEABLE,Entry->ResourceSize);
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}
}


void DoomObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                             Thing Class                                º
// º                                                                        º
// º Add/edit/delete things.                                                º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomThingObject::DoomThingObject ()
{
	// Load in the current resource and spare room for more.
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Thing) * long (NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	Data = (Thing *) MemPointer;
	ThingPointer = this;
}


DoomThingObject::DoomThingObject (int FileHandle,DirectoryEntry * Entry)
{
	// Load in the current resource and spare room for more.
	NoOfObjects = int (Entry->ResourceSize / (long) sizeof (Thing));
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Thing) * long (NoOfObjects + NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}

	Data = (Thing *) MemPointer;
	ThingPointer = this;
}


void DoomThingObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	Entry->ResourceSize = (long) sizeof (Thing) * long (NoOfObjects);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}


int DoomThingObject::Add (int EntryNo,int X,int Y)
{
	// Copy old entry if available.
	if (EntryNo >= 0)
		Data [NoOfObjects] = Data [EntryNo];
	else
	{
		Data [NoOfObjects].Angle = 0;
		Data [NoOfObjects].Type = 1;
		Data [NoOfObjects].Bitset = 7;
	}

	Data [NoOfObjects].X = X;
	Data [NoOfObjects].Y = Y;
	NoOfObjects++;
	NoOfSpareObjects--;
	
	return NoOfObjects - 1;
}


void DoomThingObject::Delete (int EntryNo)
{
	if (EntryNo < NoOfObjects - 1)
		Data [EntryNo] = Data [NoOfObjects - 1];
	
	NoOfObjects--;
	NoOfSpareObjects++;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                             Vertex Class                               º
// º                                                                        º
// º Allow use of vertex data.                                              º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomVertexObject::DoomVertexObject ()
{
	// Load in the current resource and spare room for more.
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Vertex) * long (NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	Data = (Vertex *) MemPointer;
	VertexPointer = this;
}


DoomVertexObject::DoomVertexObject (int FileHandle,DirectoryEntry * Entry)
{
	// Load in the current resource and spare room for more.
	NoOfObjects = int (Entry->ResourceSize / (long) sizeof (Vertex));
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Vertex) * long (NoOfObjects + NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}

	Data = (Vertex *) MemPointer;
	VertexPointer = this;
}


void DoomVertexObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	Entry->ResourceSize = (long) sizeof (Vertex) * long (NoOfObjects);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}


int DoomVertexObject::Add (int X,int Y)
{
	// Create a new object.
	Data [NoOfObjects].X = X;
	Data [NoOfObjects].Y = Y;
	NoOfObjects++;
	NoOfSpareObjects--;
	
	return NoOfObjects - 1;
}


void DoomVertexObject::Delete (int EntryNo)
{
	int		Loop;
	
	// Now we delete our own entry.
	if (EntryNo < NoOfObjects - 1)
	{
		// Copy end entry to the space in the array.
		Data [EntryNo] = Data [NoOfObjects - 1];
		
		// Now update all references to the last vertex in the array.
		for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
		{
			if (LineDefPointer->Data [Loop].FromVertex == NoOfObjects - 1)
				LineDefPointer->Data [Loop].FromVertex = EntryNo;
			if (LineDefPointer->Data [Loop].ToVertex == NoOfObjects - 1)
				LineDefPointer->Data [Loop].ToVertex = EntryNo;
		}
	}
	
	NoOfObjects--;
	NoOfSpareObjects++;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                           SideDef Class                                º
// º                                                                        º
// º                                                                        º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomSideDefObject::DoomSideDefObject ()
{
	// Load in the current resource and spare room for more.
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (SideDef) * long (NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	Data = (SideDef *) MemPointer;
	SideDefPointer = this;
}


DoomSideDefObject::DoomSideDefObject (int FileHandle,DirectoryEntry * Entry)
{
	// Load in the current resource and spare room for more.
	NoOfObjects = int (Entry->ResourceSize / (long) sizeof (SideDef));
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (SideDef) * long (NoOfObjects + NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}

	Data = (SideDef *) MemPointer;
	SideDefPointer = this;
}


void DoomSideDefObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	Entry->ResourceSize = (long) sizeof (SideDef) * long (NoOfObjects);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}


int DoomSideDefObject::Add ()
{
	// Create a new object.
	Data [NoOfObjects].X = 0;
	Data [NoOfObjects].Y = 0;
	strcpy (Data [NoOfObjects].Above,"-");
	strcpy (Data [NoOfObjects].Below,"-");
	strcpy (Data [NoOfObjects].Wall,"STARTAN3");
	Data [NoOfObjects].Sector = -1;

	NoOfObjects++;
	NoOfSpareObjects--;
	
	return NoOfObjects - 1;
}


void DoomSideDefObject::Delete (int EntryNo)
{
	int		Loop;
	
	// Remove all linedef references to this sidedef.
	for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
	{
		if (LineDefPointer->Data [Loop].Sidedef1 == EntryNo)
			LineDefPointer->Data [Loop].Sidedef1 = -1;
		if (LineDefPointer->Data [Loop].Sidedef2 == EntryNo)
			LineDefPointer->Data [Loop].Sidedef2 = -1;
	}
	
	// Now we delete our own entry.
	if (EntryNo < NoOfObjects - 1)
	{
		Data [EntryNo] = Data [NoOfObjects - 1];
		for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
		{
			// Swap the end sidedef for the middle one.
			if (LineDefPointer->Data [Loop].Sidedef1 == NoOfObjects - 1)
				LineDefPointer->Data [Loop].Sidedef1 = EntryNo;
			if (LineDefPointer->Data [Loop].Sidedef2 == NoOfObjects - 1)
				LineDefPointer->Data [Loop].Sidedef2 = EntryNo;
		}
	}
	
	NoOfObjects--;
	NoOfSpareObjects++;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                             LineDef Class                              º
// º                                                                        º
// º Change the linedefs.                                                   º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomLineDefObject::DoomLineDefObject ()
{
	// Load in the current resource and spare room for more.
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (LineDef) * long (NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	Data = (LineDef *) MemPointer;
	LineDefPointer = this;
}


DoomLineDefObject::DoomLineDefObject (int FileHandle,DirectoryEntry * Entry)
{
	NoOfObjects = int (Entry->ResourceSize / (long) sizeof (LineDef));
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (LineDef) * long (NoOfObjects + NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}
	
	Data = (LineDef *) MemPointer;
	LineDefPointer = this;
}


void DoomLineDefObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	Entry->ResourceSize = (long) sizeof (LineDef) * long (NoOfObjects);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}


int DoomLineDefObject::Add (int FromVertex,int ToVertex)
{
	int		Loop;
	
	// If a linedef with these vertexes exists then skip the add.
	for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
	{
		// Copy of line going the same way.
		if (LineDefPointer->Data [Loop].FromVertex == FromVertex && LineDefPointer->Data [Loop].ToVertex == ToVertex)
			return -1;
			
		// Copy of line going the other way.
		if (LineDefPointer->Data [Loop].ToVertex == FromVertex && LineDefPointer->Data [Loop].FromVertex == ToVertex)
			return -1;
	}
	
	// Create a new object.
	Data [NoOfObjects].FromVertex = FromVertex;
	Data [NoOfObjects].ToVertex = ToVertex;
	Data [NoOfObjects].Bitset = 1;
	Data [NoOfObjects].Types = 0;
	Data [NoOfObjects].Trigger = 0;
	Data [NoOfObjects].Sidedef1 = SideDefPointer->Add ();
	Data [NoOfObjects].Sidedef2 = -1;

	NoOfObjects++;
	NoOfSpareObjects--;
	
	return NoOfObjects - 1;
}


void DoomLineDefObject::Delete (int EntryNo)
{
	// Delete the sidedefs.
	SideDefPointer->Delete (LineDefPointer->Data [EntryNo].Sidedef1);
	if (LineDefPointer->Data [EntryNo].Sidedef2 >= 0)
		SideDefPointer->Delete (LineDefPointer->Data [EntryNo].Sidedef2);
	
	// Copy last entry to this entry to save it.
	if (EntryNo < NoOfObjects - 1)
		Data [EntryNo] = Data [NoOfObjects - 1];
	
	NoOfObjects--;
	NoOfSpareObjects++;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                            Sector Class                                º
// º                                                                        º
// º                                                                        º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

DoomSectorObject::DoomSectorObject ()
{
	// Load in the current resource and spare room for more.
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Sector) * long (NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	Data = (Sector *) MemPointer;
	SectorPointer = this;
}


DoomSectorObject::DoomSectorObject (int FileHandle,DirectoryEntry * Entry)
{
	NoOfObjects = int (Entry->ResourceSize / (long) sizeof (Sector));
	MemHandle = GlobalAlloc (GMEM_MOVEABLE,(long) sizeof (Sector) * long (NoOfObjects + NoOfSpareObjects));
	MemPointer = (char *) GlobalLock (MemHandle);
	if (MemPointer)
	{
		lseek (FileHandle,Entry->ResourcePointer,SEEK_SET);
		lread (FileHandle,MemPointer,Entry->ResourceSize);
	}

	Data = (Sector *) MemPointer;
	SectorPointer = this;
}


void DoomSectorObject::SaveObject (int FileHandle,DirectoryEntry * Entry)
{
	Entry->ResourcePointer = tell (FileHandle);
	Entry->ResourceSize = (long) sizeof (Sector) * long (NoOfObjects);
	lwrite (FileHandle,MemPointer,Entry->ResourceSize);
}


int DoomSectorObject::Add ()
{
	// Create a new object.
	Data [NoOfObjects].FloorHeight = 0;
	Data [NoOfObjects].CeilingHeight = 128;
	Data [NoOfObjects].Brightness = 255;
	Data [NoOfObjects].Special = 0;
	Data [NoOfObjects].Trigger = 0;
	strncpy (Data [NoOfObjects].FloorTexture,"FLOOR4_8",8);
	strncpy (Data [NoOfObjects].CeilingTexture,"CEIL3_5",8);

	NoOfObjects++;
	NoOfSpareObjects--;
	
	return NoOfObjects - 1;
}


void DoomSectorObject::Delete (int EntryNo)
{
	int		Loop;
	
	// Update the pointers to the sector.
	for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
	{
		// Remove all right sidedefs with this as their sector.
		if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector == EntryNo)
			SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector = -1;

		// Remove all left sidedefs with this sector as theirs.
		if (LineDefPointer->Data [Loop].Sidedef2 >= 0)
			if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector == EntryNo)
				SideDefPointer->Delete (LineDefPointer->Data [Loop].Sidedef2);
	}

	// Copy bottom sector to the deleted entry if needed.
	if (EntryNo < NoOfObjects - 1)
	{
		Data [EntryNo] = Data [NoOfObjects - 1];
		for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
		{
			if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector == NoOfObjects - 1)
				SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector = EntryNo;
			if (LineDefPointer->Data [Loop].Sidedef2 >= 0)
				if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector == NoOfObjects - 1)
					SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector = EntryNo;
		}
	}
	
	NoOfObjects--;
	NoOfSpareObjects++;
}
