#include "doom.h"
#include "gdredit.h"
#include <mem.h>
#include <stdio.h>
#include <commdlg.h>
#include <io.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
#include <fstream.h>


#define BUFFER_SIZE	132
#define MAX_THINGS			120
#define MAX_UNKNOWNS		40
#define MainClassName 		"GDREdit"
#define MAX_OBJECTS			50
#define VERTEX_SIZE			3
#define RED_COLOUR			RGB (255,0,0)
#define YELLOW_COLOUR		RGB (255,255,95)
#define BLUE_COLOUR			RGB (0,0,160)

#define SECTION_NAME		"CONFIG"
#define PROFILE_FILE		"GDREDIT.INI"

// Edit modes.
#define THING_EDIT			1
#define VERTEX_EDIT			2
#define LINEDEF_EDIT		3
#define SECTOR_EDIT			4

#define REDRAW_MESSAGE		WM_USER + 1
#define ADD_MESSAGE			WM_USER + 2
#define DELETE_MESSAGE		WM_USER + 3


// Our fabulous global variable area.
HWND 					hInst;
HWND 					hWndMain;
HWND					hWndButtonBar;
HWND					hWndThing;
HWND					hWndSector;
HWND					hWndLineDef;
HWND					hWndSideDef;
Header					DoomHeader;
DirectoryEntry *		DoomDirectory = NULL;
DoomObject *			DoomObjects [MAX_OBJECTS];
int						StartObject;
int						EndObject;
int						NoOfDoomObjects;
int						CurrentThing = -1;
int						CurrentVertex = -1;
int						CurrentSector = -1;
int						CurrentLineDef = -1;
int						CurrentLeftDef = -1;
int						CurrentRightDef = -1;
int						LastLineDef = -1;
int						UseSide1;
int						SectorOn = FALSE;
int						Scale;
int						OffsetX;
int						OffsetY;
int						ThingSize;
int						MouseDown = FALSE;
int						MoveMode = FALSE;
char					FileName [20];
int						EditMode = THING_EDIT;
int						ObjectList [512];
int						NoOfListObjects;
int						GridSnap;
int						GridSize;
HPEN					YellowPen;
HPEN					RedPen;
HPEN					BluePen;
HBRUSH					RedBrush;
HBRUSH					BlueBrush;
int						LineDefAddMode;
int						SectorAddMode;
int						FromVertex;
int						ToVertex;
char					DoomWadName [80];
char					DoomWadPath [80];
int						Registered = FALSE;


BOOL FAR PASCAL ChooseLevelProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_INITDIALOG:
    	{
    		char	Text [10];
    		int		Loop;
    		
			// Load the level names into the list box.
			for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
			{
				if (DoomDirectory [Loop].ResourceName [0] == 'E')
				{
					strncpy (Text,DoomDirectory [Loop].ResourceName,8);
					SendDlgItemMessage (hWndDlg,10,LB_ADDSTRING,0,(LONG) Text);
				}
			}
			
			SendDlgItemMessage (hWndDlg,10,LB_SETCURSEL,0,0);
		}
   		break;

    	case WM_CLOSE:
         	PostMessage (hWndDlg,WM_COMMAND,IDCANCEL,0L);
         	break;

    	case WM_COMMAND:
        	switch (wParam)
           	{
            	case 10: // The list box.
            		if (HIWORD (lParam) == LBN_DBLCLK)
            			SendMessage (hWndDlg,WM_COMMAND,IDOK,0L);
            		break;
            		
            	case IDOK:
            	{
            		char	Text [10];
            		int		Loop;
            		int		Loop2;
            		int		Done;
            		
            		// Set up the globals to whichever level was selected.
					SendDlgItemMessage (hWndDlg,10,LB_GETTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,LB_GETCURSEL,0,0L),(LONG) Text);
					for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
					{
						if (strncmp (DoomDirectory [Loop].ResourceName,Text,8) == 0)
						{
							StartObject = Loop;
							EndObject = (int) DoomHeader.NumberOfEntries;
							Loop2 = Loop + 1;
							Done = FALSE;
							do
							{
								if (DoomDirectory [Loop2].ResourceName [0] == 'E')
								{
									EndObject = Loop2;
									Done = TRUE;
								}
								Loop2++;
							} while ((Loop2 < DoomHeader.NumberOfEntries) && (!Done));
						}
					}
            		
                 	EndDialog (hWndDlg,TRUE);
                 }
                 break;
                 	
            	case IDCANCEL:
                 	EndDialog (hWndDlg,FALSE);
                 	break;
           	}
         	break;

    	default:
    		return FALSE;
	}
	return TRUE;
}


void UnloadWadFile ()
{
	int		Loop;
	
	// Delete all our objects in the system.
	for (Loop = 0;Loop < NoOfDoomObjects;Loop++)
		delete DoomObjects [Loop];

	// Get rid of the directory.
	if (DoomDirectory)
	{
		delete [] DoomDirectory;
		DoomDirectory = NULL;
	}
	
	// Reset the globals.
	StartObject = 0;
	EndObject = 0;
	NoOfDoomObjects = 0;
	CurrentThing = -1;
	CurrentLineDef = -1;
	LastLineDef = -1;
	CurrentSector = -1;
	CurrentVertex = -1;
	CurrentLeftDef = -1;
	CurrentRightDef = -1;
	NoOfListObjects = 0;
	FileName [0] = '\000';

	// Reset the drawing area or there will be divide by zero errors
	// when editing new wads.
	Scale = 8;
	OffsetX = 0;
	OffsetY = 0;
}


void ReadWadFile (char * WadFileName)
{
	int			FileHandle;
	int			Done;
	int			Loop;
	FARPROC		MsgProc;
	int			Result;
	int			NoOfLevels = 0;
	
	// Unload current wad file if one exists.
	UnloadWadFile ();

	// Read in the header and directory with room to add entries.
	FileHandle = open (WadFileName,O_BINARY|O_RDONLY);
	read (FileHandle,&DoomHeader,sizeof (Header));
	DoomDirectory = new DirectoryEntry [DoomHeader.NumberOfEntries + 100];
	lseek (FileHandle,DoomHeader.DirectoryPointer,SEEK_SET);
	lread (FileHandle,DoomDirectory,sizeof (DirectoryEntry) * DoomHeader.NumberOfEntries);

	// Load the level names into the list box.
	for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
	{
		if (DoomDirectory [Loop].ResourceName [0] == 'E')
			NoOfLevels++;
	}

	// Display a list of levels and select one.
	if (NoOfLevels > 1)
	{
		MsgProc = MakeProcInstance ((FARPROC) ChooseLevelProc,hInst);
		Result = DialogBox (hInst,(LPSTR) "CHOOSE_BOX",hWndMain,MsgProc);
		FreeProcInstance (MsgProc);
		if (!Result)
			return;
	}
	else
	{
		// Only one level. Load all the stuff in.
		EndObject = (int) DoomHeader.NumberOfEntries;
	}
	
	// Load up our selected level.
	for (Loop = StartObject;Loop < EndObject;Loop++)
	{
		Done = FALSE;
		
		if (strncmp (DoomDirectory [Loop].ResourceName,"VERTEXES",8) == 0)
		{
			int		MinX = 32000;
			int		MinY = 32000;
			int		MaxX = -32000;
			int		MaxY = -32000;
			int		InnerLoop;
			
			Done = TRUE;
			DoomObjects [NoOfDoomObjects] = new DoomVertexObject (FileHandle,&DoomDirectory [Loop]);
			VertexPointer = (DoomVertexObject *) DoomObjects [NoOfDoomObjects];
			if (VertexPointer->NoOfObjects > 0)
				CurrentVertex = 0;

			// Now find a good viewing point to survey our kingdom from.
			Scale = 8;
			for (InnerLoop = 0;InnerLoop < VertexPointer->NoOfObjects;InnerLoop++)
			{
				MinX = min (VertexPointer->Data [InnerLoop].X,MinX);
				MinY = min (VertexPointer->Data [InnerLoop].Y,MinY);
				MaxX = max (VertexPointer->Data [InnerLoop].X,MaxX);
				MaxY = max (VertexPointer->Data [InnerLoop].Y,MaxY);
			}
			OffsetX = -(MinX + MaxX) / 2 + (400 * Scale);
			OffsetY = -(MinY + MaxY) / 2 - (300 * Scale);
		}
			
		if (strncmp (DoomDirectory [Loop].ResourceName,"LINEDEFS",8) == 0)
		{
			Done = TRUE;
			DoomObjects [NoOfDoomObjects] = new DoomLineDefObject (FileHandle,&DoomDirectory [Loop]);
			if (LineDefPointer->NoOfObjects > 0)
				CurrentLineDef = 0;
		}
		
		if (strncmp (DoomDirectory [Loop].ResourceName,"THINGS",8) == 0)
		{
			Done = TRUE;
			DoomObjects [NoOfDoomObjects] = new DoomThingObject (FileHandle,&DoomDirectory [Loop]);
			if (ThingPointer->NoOfObjects > 0)
				CurrentThing = 0;
		}
		
		if (strncmp (DoomDirectory [Loop].ResourceName,"SIDEDEFS",8) == 0)
		{
			Done = TRUE;
			DoomObjects [NoOfDoomObjects] = new DoomSideDefObject (FileHandle,&DoomDirectory [Loop]);
		}
		
		if (strncmp (DoomDirectory [Loop].ResourceName,"SECTORS",8) == 0)
		{
			Done = TRUE;
			DoomObjects [NoOfDoomObjects] = new DoomSectorObject (FileHandle,&DoomDirectory [Loop]);
			if (SectorPointer->NoOfObjects > 0)
				CurrentSector = 0;
		}
		
		if (!Done)
			DoomObjects [NoOfDoomObjects] = new DoomObject (FileHandle,&DoomDirectory [Loop]);
		NoOfDoomObjects++;
	}

	// Fill the dialogs with some default info.
	SendMessage (hWndThing,REDRAW_MESSAGE,0,0L);
	SendMessage (hWndSector,REDRAW_MESSAGE,0,0L);
	SendMessage (hWndLineDef,REDRAW_MESSAGE,0,0L);
	
	// Clean up.
	close (FileHandle);
}


void WriteWadFile (char * WadFileName)
{
	int		FileHandle;
	int		Loop;
	
	// Write the header to hold a place for the real header.
	FileHandle = open (WadFileName,O_BINARY|O_WRONLY|O_TRUNC|O_CREAT);
	strncpy (DoomHeader.Signature,"PWAD",4);
	DoomHeader.NumberOfEntries = NoOfDoomObjects;
	write (FileHandle,&DoomHeader,sizeof (Header));

	// Save all the objects.
	for (Loop = 0;Loop < NoOfDoomObjects;Loop++)
		DoomObjects [Loop]->SaveObject (FileHandle,&DoomDirectory [StartObject + Loop]);

	// Pump out the directory, then a REAL copy of the header.
	DoomHeader.DirectoryPointer = tell (FileHandle);
	lwrite (FileHandle,&DoomDirectory [StartObject],(long) sizeof (DirectoryEntry) * (long) NoOfDoomObjects);
	lseek (FileHandle,0L,SEEK_SET);
	write (FileHandle,&DoomHeader,sizeof (Header));
	
	// Clean up.
	close (FileHandle);
}


void NewWadFile ()
{
	// Unload current wad file if one exists.
	UnloadWadFile ();
	SetWindowText (hWndMain,"GDR Edit - NEW.WAD");
	strcpy (FileName,"NEW.WAD");

	// Set up the header.
	strncpy (DoomHeader.Signature,"PWAD",4);
	DoomHeader.NumberOfEntries = 11;
	NoOfDoomObjects = 11;
	
	// Set up the directory with spare room in it.
	DoomDirectory = new DirectoryEntry [DoomHeader.NumberOfEntries + 100];
	memset (DoomDirectory,0,(int (DoomHeader.NumberOfEntries) + 100) * sizeof (DirectoryEntry));

	//  Load up the values for a base level.
	strncpy (DoomDirectory [0].ResourceName,"E1M1",8);
	DoomObjects [0] = new DoomObject ();
	strncpy (DoomDirectory [1].ResourceName,"THINGS",8);
	DoomObjects [1] = new DoomThingObject ();
	strncpy (DoomDirectory [2].ResourceName,"LINEDEFS",8);
	DoomObjects [2] = new DoomLineDefObject ();
	strncpy (DoomDirectory [3].ResourceName,"SIDEDEFS",8);
	DoomObjects [3] = new DoomSideDefObject ();
	strncpy (DoomDirectory [4].ResourceName,"VERTEXES",8);
	DoomObjects [4] = new DoomVertexObject ();
	strncpy (DoomDirectory [5].ResourceName,"SEGS",8);
	DoomObjects [5] = new DoomObject ();
	strncpy (DoomDirectory [6].ResourceName,"SSECTORS",8);
	DoomObjects [6] = new DoomObject ();
	strncpy (DoomDirectory [7].ResourceName,"NODES",8);
	DoomObjects [7] = new DoomObject ();
	strncpy (DoomDirectory [8].ResourceName,"SECTORS",8);
	DoomObjects [8] = new DoomSectorObject ();
	strncpy (DoomDirectory [9].ResourceName,"REJECT",8);
	DoomObjects [9] = new DoomObject ();
	strncpy (DoomDirectory [10].ResourceName,"BLOCKMAP",8);
	DoomObjects [10] = new DoomObject ();
			
	// Fill the dialogs with some default info.
	InvalidateRect (hWndMain,NULL,TRUE);
	SendMessage (hWndThing,REDRAW_MESSAGE,0,0L);
	SendMessage (hWndSector,REDRAW_MESSAGE,0,0L);
	SendMessage (hWndLineDef,REDRAW_MESSAGE,0,0L);
}


void LoadWadFile ()
{
	OPENFILENAME	FileOpenDialog;
	char			Filter [] = "Data files (*.wad)\0*.wad\0All files (*.*)\0*.*\0";
	char			Text [80];
	char			FileName [20];

	// Setup for the common file open dialog.
	memset (&FileOpenDialog,0,sizeof (FileOpenDialog));
	FileOpenDialog.lStructSize = sizeof (FileOpenDialog);
	FileOpenDialog.hWndOwner = hWndMain;
	FileOpenDialog.hInstance = hInst;
	FileOpenDialog.lpstrFilter = Filter;
	FileOpenDialog.nFilterIndex = 1;
	FileOpenDialog.Flags = OFN_PATHMUSTEXIST | OFN_FILEMUSTEXIST;
	FileOpenDialog.lpstrFileTitle = FileName;
	
	if (GetOpenFileName (&FileOpenDialog))
	{
		// Found a filename, process the file.
		ReadWadFile (FileName);
		sprintf (Text,"GDR Edit - %s",FileName);
		SetWindowText (hWndMain,Text);
		InvalidateRect (hWndMain,NULL,TRUE);
	}
}


void SaveWadFile ()
{
	OPENFILENAME	FileSaveDialog;
	char			Text [80];

	// Setup for the common file open dialog.
	memset (&FileSaveDialog,0,sizeof (FileSaveDialog));
	FileSaveDialog.lStructSize = sizeof (FileSaveDialog);
	FileSaveDialog.hWndOwner = hWndMain;
	FileSaveDialog.hInstance = hInst;
	FileSaveDialog.lpstrFilter = "Data files (*.wad)\0*.wad\0All files (*.*)\0*.*\0";
	FileSaveDialog.lpstrDefExt = "WAD";
	FileSaveDialog.nFilterIndex = 1;
	FileSaveDialog.Flags = OFN_PATHMUSTEXIST;
	FileSaveDialog.lpstrFileTitle = FileName;
	
	// If we get a name, save the file.
	if (GetSaveFileName (&FileSaveDialog))
	{
		WriteWadFile (FileName);
		sprintf (Text,"GDR Edit - %s",FileName);
		SetWindowText (hWndMain,Text);
	}
}


void DrawLineDef (HDC DC,int EntryNo)
{
	int		MidX;
	int		MidY;
	int		EndX;
	int		EndY;
	int		Trim;
	
	// Draw the line itself.
	MoveTo (DC,(VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].X + OffsetX) / Scale,-(VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].Y + OffsetY) / Scale);
	LineTo (DC,(VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].X + OffsetX) / Scale,-(VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].Y + OffsetY) / Scale);
	
	// Place the tick mark.
	if (EditMode == SECTOR_EDIT || EditMode == LINEDEF_EDIT)
	{
		// Get the midpoints.
		MidX = (VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].X + VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].X) / 2;
		MidY = (VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].Y + VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].Y) / 2;
	
		// Get the deltas.
		EndX = VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].Y - VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].Y;
		EndY = VertexPointer->Data [LineDefPointer->Data [EntryNo].FromVertex].X - VertexPointer->Data [LineDefPointer->Data [EntryNo].ToVertex].X;

		// Trim the deltas to a maximum value.
		if (abs (EndX) >= abs (EndY))
			Trim = abs (EndX) / 16;
		else
			Trim = abs (EndY) / 16;
		if (Trim == 0)
			Trim = 1;
		EndX /= Trim;
		EndY /= Trim;
	
		// Add our delta values.
		EndX += MidX;
		EndY += MidY;

		MoveTo (DC,(MidX + OffsetX) / Scale,-(MidY + OffsetY) / Scale);
		LineTo (DC,(EndX + OffsetX) / Scale,-(EndY + OffsetY) / Scale);
	}
}


void PaintIt (HDC DC)
{
	int		Loop;
	RECT	Rect;

	// Setup for the draw.
	SetROP2 (DC,R2_NOTMERGEPEN);

	// Draw some linedefs.
	if (LineDefPointer && VertexPointer)
	{
		for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
			DrawLineDef (DC,Loop);
	}

	// Draw some vertexes.
	if (VertexPointer && (EditMode == VERTEX_EDIT || EditMode == LINEDEF_EDIT || EditMode == SECTOR_EDIT))
	{
		for (Loop = 0;Loop < VertexPointer->NoOfObjects;Loop++)
		{
			SetRect (&Rect,(VertexPointer->Data [Loop].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [Loop].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale + VERTEX_SIZE);
			InvertRect (DC,&Rect);
		}
	}
	
	// Draw some things.
	if (ThingPointer && (EditMode == THING_EDIT))
	{
		// Set the size a thing should be drawn at.
		ThingSize = 16 / Scale;
		if (ThingSize < 2)
			ThingSize = 2;
			
		// Draw all the things.
		for (Loop = 0;Loop < ThingPointer->NoOfObjects;Loop++)
		{
			SetRect (&Rect,(ThingPointer->Data [Loop].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [Loop].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale + ThingSize);
			InvertRect (DC,&Rect);
		}
	}

	// Draw the highlighted linedef.
	SetROP2 (DC,R2_COPYPEN);
	SelectObject (DC,RedPen);
	if (LineDefPointer && EditMode == LINEDEF_EDIT && CurrentLineDef >= 0)
		DrawLineDef (DC,CurrentLineDef);

	// If we have a sector then we should highlight it.
	if (SectorPointer && EditMode == SECTOR_EDIT && CurrentSector >= 0)
		for (Loop = 0;Loop < NoOfListObjects;Loop++)
			DrawLineDef (DC,ObjectList [Loop]);
}


BOOL FAR PASCAL ButtonBarMsgProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_COMMAND:
        	switch (wParam)
           	{
				case 202:
					// Up arrow.
					OffsetY -= 20 * Scale;
					InvalidateRect (hWndMain,NULL,TRUE);
					break;

				case 203:
					// Down arrow.
					OffsetY += 20 * Scale;
					InvalidateRect (hWndMain,NULL,TRUE);
					break;

				case 204:
					// Left arrow.
					OffsetX += 20 * Scale;
					InvalidateRect (hWndMain,NULL,TRUE);
					break;

				case 205:
					// Right arrow.
					OffsetX -= 20 * Scale;
					InvalidateRect (hWndMain,NULL,TRUE);
					break;
					
				case 206: // Zoom in.
					// Zoom in for detail work.
					if (Scale > 1)
					{
						Scale--;
						OffsetX -= 400;
						OffsetY += 300;
						InvalidateRect (hWndMain,NULL,TRUE);
					}
					break;
					
				case 207: // Zoom out
					// Zoom out for overall view.
					if (Scale < 20)
					{
						Scale++;
						OffsetX += 400;
						OffsetY -= 300;
						InvalidateRect (hWndMain,NULL,TRUE);
					}
					break;

				case 212:
					// Add something.
					SendMessage (hWndMain,ADD_MESSAGE,0,0L);
					break;
					
				case 213:
					// Delete something.
					SendMessage (hWndMain,DELETE_MESSAGE,0,0L);
					break;
           	}
         	break;

    	default:
			return FALSE;
	}
	return TRUE;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                            Thing Editing                               º
// º                                                                        º
// º Allow the adding, moving, changing and deleting of things in the .WAD  º
// º file.                                                                  º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

int FindThatThing (int X,int Y)
{
	int		Loop;
	long	DX;
	long	DY;
	long	Min = 1000000L;
	long	Value;
	int		Selected = -1;
	RECT	Rect;
	POINT	Point;

	Point.x = X;
	Point.y = Y;
		
	if (ThingPointer)
	{
		// Check for an exact hit first.
		for (Loop = 0;Loop < ThingPointer->NoOfObjects;Loop++)
		{
			SetRect (&Rect,(ThingPointer->Data [Loop].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [Loop].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale + ThingSize);
			if (PtInRect (&Rect,Point))
				return Loop;
		}
		
		// Try for the nearest instead.
		for (Loop = 0;Loop < ThingPointer->NoOfObjects;Loop++)
		{
			DX = (long) (ThingPointer->Data [Loop].X + OffsetX) / Scale - X;
			DY = (long) -(ThingPointer->Data [Loop].Y + OffsetY) / Scale - Y;
			Value = DX * DX + DY * DY;
			if (Value < Min)
			{
				Min = Value;
				Selected = Loop;
			}
		}
	}
	
	return Selected;
}


BOOL FAR PASCAL ThingProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
		{
    	case WM_INITDIALOG:
    	{
			fstream		InActivityFile;
			char		Buffer [BUFFER_SIZE];
			char		Text [BUFFER_SIZE];
			int			ItemType;
			char *		CopyFrom;
		    		
			InActivityFile.open ("GDREDIT.INI",ios::in|ios::nocreate);
			if (!InActivityFile.fail ())
			{
				// Get to the start of the section we want.
				do
				{
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				} while (!InActivityFile.eof () && strcmp (Buffer,"[THINGS]") != 0);
				InActivityFile.getline (Buffer,BUFFER_SIZE);
				
				while (!InActivityFile.eof () && Buffer [0] != '[')
				{
					if (strlen (Buffer) > 0)
					{
						ItemType = atoi (Buffer);
						CopyFrom = strstr (Buffer,",");
						if (CopyFrom != NULL)
							strcpy (Text,CopyFrom + 1);
						SendDlgItemMessage (hWndDlg,10,CB_SETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,10,CB_ADDSTRING,0,(LONG) Text),(LONG) ItemType);
					}
				
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				}
				InActivityFile.close ();
			}
			SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,0,0);
		}
   		break;
    		
    	case REDRAW_MESSAGE: // Update the window.
    	{
    		int		Loop;
    		int		ListCount;
    		int		Finished = FALSE;
    		int		Entry = -1;
    		
    		// Update our display.
    		if (ThingPointer && CurrentThing >= 0)
    		{
				Loop = -1;
				ListCount = (int) SendDlgItemMessage (hWndDlg,10,CB_GETCOUNT,0,0L);
				do
				{
					Loop++;
					if (SendDlgItemMessage (hWndDlg,10,CB_GETITEMDATA,Loop,0) == ThingPointer->Data [CurrentThing].Type)
					{
						Finished = TRUE;
						Entry = Loop;
					}
				} while ((!Finished) && (Loop < ListCount - 1));
				SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,Entry,0);
					
				CheckDlgButton (hWndDlg,11,ThingPointer->Data [CurrentThing].Bitset & 1);
				CheckDlgButton (hWndDlg,12,ThingPointer->Data [CurrentThing].Bitset & 2);
				CheckDlgButton (hWndDlg,13,ThingPointer->Data [CurrentThing].Bitset & 4);
				CheckDlgButton (hWndDlg,14,ThingPointer->Data [CurrentThing].Bitset & 8);
				CheckDlgButton (hWndDlg,15,ThingPointer->Data [CurrentThing].Bitset & 16);
				SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
    		}
    	}
    	break;
    	
    	case WM_COMMAND:
    		if (ThingPointer && CurrentThing >= 0)
    		{
	        	switch (wParam)
	           	{
					case 10: // Type of object.
	            		if (HIWORD (lParam) == CBN_SELCHANGE)
							ThingPointer->Data [CurrentThing].Type = (int) SendDlgItemMessage (hWndDlg,10,CB_GETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),0);
						break;
						
					case 11: // Level 1
						if (ThingPointer->Data [CurrentThing].Bitset & 1)
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset - 1;
						else
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset + 1;
						break;

					case 12: // Levels 2 & 3
						if (ThingPointer->Data [CurrentThing].Bitset & 2)
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset - 2;
						else
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset + 2;
						break;

					case 13: // Levels 4 & 5
						if (ThingPointer->Data [CurrentThing].Bitset & 4)
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset - 4;
						else
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset + 4;
						break;

					case 14: // Deaf bugger.
						if (ThingPointer->Data [CurrentThing].Bitset & 8)
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset - 8;
						else
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset + 8;
						break;

					case 15: // Net game.
						if (ThingPointer->Data [CurrentThing].Bitset & 16)
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset - 16;
						else
							ThingPointer->Data [CurrentThing].Bitset = ThingPointer->Data [CurrentThing].Bitset + 16;
						break;

					case 204:
						ThingPointer->Data [CurrentThing].Angle = 0;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 208:
						ThingPointer->Data [CurrentThing].Angle = 45;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 202:
						ThingPointer->Data [CurrentThing].Angle = 90;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 209:
						ThingPointer->Data [CurrentThing].Angle = 135;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 205:
						ThingPointer->Data [CurrentThing].Angle = 180;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 210:
						ThingPointer->Data [CurrentThing].Angle = 225;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 203:
						ThingPointer->Data [CurrentThing].Angle = 270;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;

					case 211:
						ThingPointer->Data [CurrentThing].Angle = 315;
						SetDlgItemInt (hWndDlg,16,ThingPointer->Data [CurrentThing].Angle,TRUE);
						break;
	           	}
           	}
         	break;

    	default:
			return FALSE;
	}
	return TRUE;
}


LONG FAR PASCAL ThingEdit (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
		case WM_RBUTTONDOWN: // They want to add/delete things.
		{
			RECT	Rect;
			POINT	Point;
			int		Done = FALSE;
			int		Loop;
			HDC		DC;

			Point.x = LOWORD (lParam);
			Point.y = HIWORD (lParam);
		
			if (ThingPointer)
			{
				// Check for an exact hit.
				for (Loop = 0;Loop < ThingPointer->NoOfObjects;Loop++)
				{
					SetRect (&Rect,(ThingPointer->Data [Loop].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [Loop].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [Loop].Y + OffsetY) / Scale + ThingSize);
					if (PtInRect (&Rect,Point))
					{
						Done = TRUE;
						CurrentThing = Loop;
						SendMessage (hWndMain,DELETE_MESSAGE,0,0L);
					}
				}
				
				// Add a new thing instead.
				if (Done == FALSE)
				{
					CurrentThing = ThingPointer->Add (CurrentThing,LOWORD (lParam) * Scale - OffsetX,-(HIWORD (lParam) * Scale + OffsetY));
					DC = GetDC (hWndMain);
					SetRect (&Rect,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale + ThingSize);
					InvertRect (DC,&Rect);
					ReleaseDC (hWndMain,DC);
					SendMessage (hWndThing,REDRAW_MESSAGE,0,0L);
				}
			}
		}
		break;
			
		case WM_LBUTTONDOWN: // They want to move/edit something.
		{
			int		Result;
			
			Result = FindThatThing (LOWORD (lParam),HIWORD (lParam));
			if (Result != -1)
			{
				CurrentThing = Result;
				SendMessage (hWndThing,REDRAW_MESSAGE,0,0L);
				MouseDown = TRUE;
			}
		}
		break;

		case WM_LBUTTONUP: // Cleanup after move.
			MoveMode = FALSE;
			MouseDown = FALSE;
			break;
	
		case WM_MOUSEMOVE: // Check for drags.
			if (MouseDown)
			{
	 			RECT	Rect;
				HDC		DC;
			
				// Erase object.
				MoveMode = TRUE;
				DC = GetDC (hWndMain);
				SetRect (&Rect,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale + ThingSize);
				InvertRect (DC,&Rect);
				
				// Update position.
				ThingPointer->Data [CurrentThing].X = LOWORD (lParam) * Scale - OffsetX;
				ThingPointer->Data [CurrentThing].Y = -(HIWORD (lParam) * Scale + OffsetY);
				
				// Redraw object at new location.
				SetRect (&Rect,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale + ThingSize);
				InvertRect (DC,&Rect);
				ReleaseDC (hWndMain,DC);
			}
			break;
		
		case DELETE_MESSAGE:
		{
			RECT	Rect;
			HDC		DC;
			
			// Erase object.
			DC = GetDC (hWndMain);
			SetRect (&Rect,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale - ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale - ThingSize,(ThingPointer->Data [CurrentThing].X + OffsetX) / Scale + ThingSize,-(ThingPointer->Data [CurrentThing].Y + OffsetY) / Scale + ThingSize);
			InvertRect (DC,&Rect);
			ThingPointer->Delete (CurrentThing);
			ReleaseDC (hWndMain,DC);
			
			// Switch to the previous thing.
			ThingPointer->Delete (CurrentThing);
			CurrentThing--;
			if (CurrentThing < 0)
				CurrentThing = ThingPointer->NoOfObjects - 1;
				
			// Update the dialog box.	
			SendMessage (hWndThing,REDRAW_MESSAGE,0,0L);
		}
		break;
					
		default:
			return DefWindowProc (hWnd,Message,wParam,lParam);
	}
	
	return 0L;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                           Vertex Editing                               º
// º                                                                        º
// º Pretty obvious.                                                        º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

int FindThatVertex (int X,int Y)
{
	int		Loop;
	RECT	Rect;
	POINT	Point;
	long	DX;
	long	DY;
	long	Min = 1000000L;
	long	Value;
	int		Selected = -1;

	Point.x = X;
	Point.y = Y;
		
	if (VertexPointer)
	{
		// Check for an exact hit first.
		for (Loop = 0;Loop < VertexPointer->NoOfObjects;Loop++)
		{
			SetRect (&Rect,(VertexPointer->Data [Loop].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [Loop].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale + VERTEX_SIZE);
			if (PtInRect (&Rect,Point))
				return Loop;
		}
		
		// Try for the nearest instead.
		for (Loop = 0;Loop < VertexPointer->NoOfObjects;Loop++)
		{
			DX = (long) (VertexPointer->Data [Loop].X + OffsetX) / Scale - X;
			DY = (long) -(VertexPointer->Data [Loop].Y + OffsetY) / Scale - Y;
			Value = DX * DX + DY * DY;
			if (Value < Min)
			{
				Min = Value;
				Selected = Loop;
			}
		}
	}
	
	return Selected;
}


LONG FAR PASCAL VertexEdit (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
		case WM_RBUTTONDOWN: // They want to add/delete vertexes.
		{
			RECT	Rect;
			POINT	Point;
			int		Done = FALSE;
			int		Loop;
			int		Loop2;
			HDC		DC;

			Point.x = LOWORD (lParam);
			Point.y = HIWORD (lParam);
		
			if (VertexPointer)
			{
				// Setup time.
				DC = GetDC (hWndMain);
				SetROP2 (DC,R2_NOTMERGEPEN);
				
				// Check for an exact hit.
				for (Loop = 0;Loop < VertexPointer->NoOfObjects;Loop++)
				{
					SetRect (&Rect,(VertexPointer->Data [Loop].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [Loop].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [Loop].Y + OffsetY) / Scale + VERTEX_SIZE);
					if (PtInRect (&Rect,Point))
					{
						// Find all linedefs that use this vertex.
						CurrentVertex = Loop;
						NoOfListObjects = 0;
						if (LineDefPointer)
						{
							// Detect which lines need updating.
							for (Loop2 = 0;Loop2 < LineDefPointer->NoOfObjects;Loop2++)
							{
								if ((LineDefPointer->Data [Loop2].FromVertex == CurrentVertex) || (LineDefPointer->Data [Loop2].ToVertex == CurrentVertex))
								{
									ObjectList [NoOfListObjects] = Loop2;
									NoOfListObjects++;
								}
							}
						}

		                // Erase and delete the current linedefs. Work
		                // backwards to avoid indexes changing before we delete
		                // a line.
						for (Loop2 = NoOfListObjects - 1;Loop2 >= 0;Loop2--)
						{
							DrawLineDef (DC,ObjectList [Loop2]);
							LineDefPointer->Delete (ObjectList [Loop2]);
						}
				
						// Erase object.
						InvertRect (DC,&Rect);
						VertexPointer->Delete (CurrentVertex);
						
						Done = TRUE;
					}
				}
				
				// Add a new vertex instead.
				if (Done == FALSE)
				{
					// Add a new vertex taking gridsnap into account.
					CurrentVertex = VertexPointer->Add (LOWORD (lParam) * Scale - OffsetX,-(HIWORD (lParam) * Scale + OffsetY));
					SetRect (&Rect,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale + VERTEX_SIZE);
					InvertRect (DC,&Rect);
				}
				
				// Cleanup.
				ReleaseDC (hWndMain,DC);
			}
		}
		break;
			
		case WM_LBUTTONDOWN:
		{
			int		Result;
			int		Loop;
			
			Result = FindThatVertex (LOWORD (lParam),HIWORD (lParam));
			if (Result != -1)
			{
				// Set some global state variables.
				CurrentVertex = Result;
				MouseDown = TRUE;

				// Now we narrow down the list of lines to update from this move.
				NoOfListObjects = 0;
				if (LineDefPointer)
				{
					// Detect which lines need updating.
					for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
					{
						if ((LineDefPointer->Data [Loop].FromVertex == CurrentVertex) || (LineDefPointer->Data [Loop].ToVertex == CurrentVertex))
						{
							ObjectList [NoOfListObjects] = Loop;
							NoOfListObjects++;
						}
					}
				}
			}
		}
		break;
			
		case WM_LBUTTONUP:
			MoveMode = FALSE;
			MouseDown = FALSE;
			break;
	
		case WM_MOUSEMOVE: // Detect guys in chicks clothes (drags).
			if (MouseDown)
			{
				int		Loop;
				RECT	Rect;
				HDC		DC;
				
				DC = GetDC (hWndMain);
				SetROP2 (DC,R2_NOTMERGEPEN);

				// Erase the vertex from the screen.
				SetRect (&Rect,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale + VERTEX_SIZE);
				InvertRect (DC,&Rect);
				
                // Erase the current linedefs.
				for (Loop = 0;Loop < NoOfListObjects;Loop++)
					DrawLineDef (DC,ObjectList [Loop]);
				
				// Update the vertex position.
				VertexPointer->Data [CurrentVertex].X = LOWORD (lParam) * Scale - OffsetX;
				VertexPointer->Data [CurrentVertex].Y = -(HIWORD (lParam) * Scale + OffsetY);
				
				// Take gridsnap int account.
				if (GridSnap)
				{
					VertexPointer->Data [CurrentVertex].X -= VertexPointer->Data [CurrentVertex].X % GridSize;
					VertexPointer->Data [CurrentVertex].Y -= VertexPointer->Data [CurrentVertex].Y % GridSize;
				}

				// Redraw the vertex.
				SetRect (&Rect,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale - VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale - VERTEX_SIZE,(VertexPointer->Data [CurrentVertex].X + OffsetX) / Scale + VERTEX_SIZE,-(VertexPointer->Data [CurrentVertex].Y + OffsetY) / Scale + VERTEX_SIZE);
				InvertRect (DC,&Rect);
				
				// Redraw all linedefs.
				for (Loop = 0;Loop < NoOfListObjects;Loop++)
					DrawLineDef (DC,ObjectList [Loop]);

				ReleaseDC (hWndMain,DC);
				MoveMode = TRUE;
			}
			break;
		
		default:
			return DefWindowProc (hWnd,Message,wParam,lParam);
	}
	
	return 0L;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                          LineDef Editing                               º
// º                                                                        º
// º Pretty obvious.                                                        º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

int FindThatLineDef (int X,int Y)
{
	int		Loop;
	int		MidX;
	int		MidY;
	long	DX;
	long	DY;
	long	Min = 1000000L;
	long	Value;
	int		Selected = -1;

	if (VertexPointer && LineDefPointer)
	{
		// Nearest to the centre of a linedef wins !
		for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
		{
			// Get the midpoints.
			MidX = (VertexPointer->Data [LineDefPointer->Data [Loop].FromVertex].X + VertexPointer->Data [LineDefPointer->Data [Loop].ToVertex].X) / 2;
			MidY = (VertexPointer->Data [LineDefPointer->Data [Loop].FromVertex].Y + VertexPointer->Data [LineDefPointer->Data [Loop].ToVertex].Y) / 2;
			
			// Work out distance from the midpoints.
			DX = (long) (MidX + OffsetX) / Scale - X;
			DY = (long) -(MidY + OffsetY) / Scale - Y;
			Value = DX * DX + DY * DY;
			if (Value < Min)
			{
				Min = Value;
				Selected = Loop;
			}
		}
	}
	
	return Selected;
}


BOOL FAR PASCAL SideDefProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_INITDIALOG:
    	{
			int		Loop;
			int		Loop2;
			char	Text [60];
			HANDLE	MemHandle;
			char *	MemPointer;
			int		FileHandle;
			int		NoOfTextures;
			
			// Open the main WAD file and check out the textures.
			FileHandle = open (DoomWadName,O_BINARY|O_RDONLY);
			if (FileHandle)
			{
				for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
				{
					if (strncmp (DoomDirectory [Loop].ResourceName,"TEXTURE",7) == 0)
					{
						// One of the texture patch maps.
						MemHandle = GlobalAlloc (GMEM_MOVEABLE,DoomDirectory [Loop].ResourceSize);
						MemPointer = (char *) GlobalLock (MemHandle);
						if (MemPointer)
						{
							lseek (FileHandle,DoomDirectory [Loop].ResourcePointer,SEEK_SET);
							lread (FileHandle,MemPointer,DoomDirectory [Loop].ResourceSize);

							// Put the strings into a list box.
							Text [8] = '\000';
							NoOfTextures = int (*(long *) MemPointer);
							for (Loop2 = 0;Loop2 < NoOfTextures;Loop2++)
							{
								strncpy (Text,MemPointer + long (*(long *) (MemPointer + 4L * long (Loop2 + 1))),8);
								SendDlgItemMessage (hWndDlg,10,CB_ADDSTRING,0,(LONG) Text);
							}

							// Free the memory.
							GlobalUnlock (MemHandle);
							GlobalFree (MemHandle);
						}
					}
				}
				close (FileHandle);
			}
			
			SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,0,0);
		}
   		break;
    		
    	case REDRAW_MESSAGE: // Update the window.
    	{
    		// Update our display.
    		if (SideDefPointer)
    		{
    			if (SideDefPointer->NoOfObjects > 0)
    			{
    				int		LeftOn;
    				char	Text [20];
    				
    				// Only make these controls accessable when the left sidedef
    				// exists.
    				LeftOn = LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0;
					EnableWindow (GetDlgItem (hWndDlg,11),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,12),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,22),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,14),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,15),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,17),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,18),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,20),LeftOn);
					EnableWindow (GetDlgItem (hWndDlg,21),LeftOn);
					
					// Fill in the right hand side.
					SetDlgItemInt (hWndDlg,23,SideDefPointer->Data [CurrentRightDef].X,TRUE);
					SetDlgItemInt (hWndDlg,24,SideDefPointer->Data [CurrentRightDef].Y,TRUE);
					SetDlgItemInt (hWndDlg,34,SideDefPointer->Data [CurrentRightDef].Sector,TRUE);
					Text [8] = '\000';
					strncpy (Text,SideDefPointer->Data [CurrentRightDef].Above,8);
					SetDlgItemText (hWndDlg,25,Text);
					strncpy (Text,SideDefPointer->Data [CurrentRightDef].Wall,8);
					SetDlgItemText (hWndDlg,28,Text);
					strncpy (Text,SideDefPointer->Data [CurrentRightDef].Below,8);
					SetDlgItemText (hWndDlg,31,Text);
					
					// Fill in the left hand side.
    				if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
    				{
						SetDlgItemInt (hWndDlg,11,SideDefPointer->Data [CurrentLeftDef].X,TRUE);
						SetDlgItemInt (hWndDlg,12,SideDefPointer->Data [CurrentLeftDef].Y,TRUE);
						SetDlgItemInt (hWndDlg,22,SideDefPointer->Data [CurrentLeftDef].Sector,TRUE);
						strncpy (Text,SideDefPointer->Data [CurrentLeftDef].Above,8);
						SetDlgItemText (hWndDlg,13,Text);
						strncpy (Text,SideDefPointer->Data [CurrentLeftDef].Wall,8);
						SetDlgItemText (hWndDlg,16,Text);
						strncpy (Text,SideDefPointer->Data [CurrentLeftDef].Below,8);
						SetDlgItemText (hWndDlg,19,Text);
					}
					else
					{
						// Blank out the left side to avoid confusion.
						SetDlgItemText (hWndDlg,11,"");
						SetDlgItemText (hWndDlg,12,"");
						SetDlgItemText (hWndDlg,22,"");
						SetDlgItemText (hWndDlg,13,"");
						SetDlgItemText (hWndDlg,16,"");
						SetDlgItemText (hWndDlg,19,"");
					}
	    		}
    		}
    	}
    	break;
    	
    	case WM_COMMAND:
    	{
    		int		Trans;
    		char	Text [10];
    		
    		if (SideDefPointer)
    		{
	        	switch (wParam)
	           	{
	           		case 1: // Ok button.
						ShowWindow (hWndSideDef,SW_HIDE);
	           			break;

					case 23: // X.
						if (HIWORD (lParam) == EN_CHANGE)
							SideDefPointer->Data [CurrentRightDef].X = GetDlgItemInt (hWndDlg,wParam,&Trans,TRUE);
						break;

					case 24: // Y.
						if (HIWORD (lParam) == EN_CHANGE)
							SideDefPointer->Data [CurrentRightDef].Y = GetDlgItemInt (hWndDlg,wParam,&Trans,TRUE);
						break;

					case 26: // Clear Above.
						strcpy (SideDefPointer->Data [CurrentRightDef].Above,"-");
						SetDlgItemText (hWndDlg,25,"-");
						break;

					case 27: // Set Above.
						SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
						strncpy (SideDefPointer->Data [CurrentRightDef].Above,Text,8);
						SetDlgItemText (hWndDlg,25,Text);
						break;

					case 29: // Clear wall.
						strcpy (SideDefPointer->Data [CurrentRightDef].Wall,"-");
						SetDlgItemText (hWndDlg,28,"-");
						break;

					case 30: // Set wall.
						SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
						strncpy (SideDefPointer->Data [CurrentRightDef].Wall,Text,8);
						SetDlgItemText (hWndDlg,28,Text);
						break;

					case 32: // Clear Below.
						strcpy (SideDefPointer->Data [CurrentRightDef].Below,"-");
						SetDlgItemText (hWndDlg,31,"-");
						break;

					case 33: // Set Below.
						SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
						strncpy (SideDefPointer->Data [CurrentRightDef].Below,Text,8);
						SetDlgItemText (hWndDlg,31,Text);
						break;

					// These apply to the left sidedef.
					case 11: // X.
						if (HIWORD (lParam) == EN_CHANGE && LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
							SideDefPointer->Data [CurrentLeftDef].X = GetDlgItemInt (hWndDlg,wParam,&Trans,TRUE);
						break;

					case 12: // Y.
						if (HIWORD (lParam) == EN_CHANGE && LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
							SideDefPointer->Data [CurrentLeftDef].Y = GetDlgItemInt (hWndDlg,wParam,&Trans,TRUE);
						break;

					case 14: // Clear Above.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							strcpy (SideDefPointer->Data [CurrentLeftDef].Above,"-");
							SetDlgItemText (hWndDlg,13,"-");
						}
						break;

					case 15: // Set Above.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
							strncpy (SideDefPointer->Data [CurrentLeftDef].Above,Text,8);
							SetDlgItemText (hWndDlg,13,Text);
						}
						break;

					case 17: // Clear wall.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							strcpy (SideDefPointer->Data [CurrentLeftDef].Wall,"-");
							SetDlgItemText (hWndDlg,16,"-");
						}
						break;

					case 18: // Set wall.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
							strncpy (SideDefPointer->Data [CurrentLeftDef].Wall,Text,8);
							SetDlgItemText (hWndDlg,16,Text);
						}
						break;

					case 20: // Clear Below.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							strcpy (SideDefPointer->Data [CurrentLeftDef].Below,"-");
							SetDlgItemText (hWndDlg,19,"-");
						}
						break;

					case 21: // Set Below.
						if (LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
						{
							SendDlgItemMessage (hWndDlg,10,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,10,CB_GETCURSEL,0,0),(LONG) Text);
							strncpy (SideDefPointer->Data [CurrentLeftDef].Below,Text,8);
							SetDlgItemText (hWndDlg,19,Text);
						}
						break;
	           	}
           	}
		}
		break;

    	default:
			return FALSE;
	}
	return TRUE;
}


BOOL FAR PASCAL LineDefProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_INITDIALOG:
    	{
			fstream		InActivityFile;
			char		Buffer [BUFFER_SIZE];
			char		Text [BUFFER_SIZE];
			int			LineType;
			char *		CopyFrom;
		    		
			// Load info into the special dialog box.
			InActivityFile.open ("GDREDIT.INI",ios::in|ios::nocreate);
			if (!InActivityFile.fail ())
			{
				// Get to the start of the section we want.
				do
				{
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				} while (!InActivityFile.eof () && strcmp (Buffer,"[LINETYPES]") != 0);
				InActivityFile.getline (Buffer,BUFFER_SIZE);
				
				while (!InActivityFile.eof () && Buffer [0] != '[')
				{
					if (strlen (Buffer) > 0)
					{
						LineType = atoi (Buffer);
						CopyFrom = strstr (Buffer,",");
						if (CopyFrom != NULL)
							strcpy (Text,CopyFrom + 1);
						SendDlgItemMessage (hWndDlg,10,CB_SETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,10,CB_ADDSTRING,0,(LONG) Text),(LONG) LineType);
					}
				
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				}
				InActivityFile.close ();
			}
			SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,0,0);
		}
   		break;
    		
    	case REDRAW_MESSAGE: // Update the window.
    	{
    		int		Loop;
    		int		Finished = FALSE;
    		int		Entry = -1;
    		int		MaxTypes;
    		
    		// Update our display.
    		if (LineDefPointer  && CurrentLineDef >= 0)
    		{
				// Set up the line type item.
				MaxTypes = (int) SendDlgItemMessage (hWndDlg,10,CB_GETCOUNT,0,0);
				Loop = -1;
				do
				{
					Loop++;
					if (SendDlgItemMessage (hWndDlg,10,CB_GETITEMDATA,Loop,0) == LineDefPointer->Data [CurrentLineDef].Types)
					{
						Finished = TRUE;
						Entry = Loop;
					}
				} while (!Finished && Loop < MaxTypes);
				SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,Entry,0);
					
				// Set up the bitsets.
				CheckDlgButton (hWndDlg,11,LineDefPointer->Data [CurrentLineDef].Bitset & 1);
				CheckDlgButton (hWndDlg,12,LineDefPointer->Data [CurrentLineDef].Bitset & 2);
				CheckDlgButton (hWndDlg,13,LineDefPointer->Data [CurrentLineDef].Bitset & 4);
				CheckDlgButton (hWndDlg,14,LineDefPointer->Data [CurrentLineDef].Bitset & 8);
				CheckDlgButton (hWndDlg,15,LineDefPointer->Data [CurrentLineDef].Bitset & 16);
				CheckDlgButton (hWndDlg,16,LineDefPointer->Data [CurrentLineDef].Bitset & 32);
				CheckDlgButton (hWndDlg,17,LineDefPointer->Data [CurrentLineDef].Bitset & 64);
				CheckDlgButton (hWndDlg,18,LineDefPointer->Data [CurrentLineDef].Bitset & 128);
				CheckDlgButton (hWndDlg,19,LineDefPointer->Data [CurrentLineDef].Bitset & 256);

				// Set up the edit fields.
				SetDlgItemInt (hWndDlg,20,LineDefPointer->Data [CurrentLineDef].Trigger,TRUE);
					
				// Set up the sidedefs.
				CurrentRightDef = LineDefPointer->Data [CurrentLineDef].Sidedef1;
				CurrentLeftDef = LineDefPointer->Data [CurrentLineDef].Sidedef2;
				SendMessage (hWndSideDef,REDRAW_MESSAGE,0,0L);
    		}
    	}
    	break;
    	
    	case WM_COMMAND:
    	{
    		int		Trans;
    		
    		if (LineDefPointer && CurrentLineDef >= 0)
    		{
	        	switch (wParam)
	           	{
	           		case 1: // Ok button.
						ShowWindow (hWndLineDef,SW_HIDE);
	           			break;

					case 10: // Line type changed.
						if (HIWORD (lParam) == CBN_SELCHANGE)
							LineDefPointer->Data [CurrentLineDef].Types = (int) SendDlgItemMessage (hWndDlg,wParam,CB_GETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,wParam,CB_GETCURSEL,0,0),0);
						break;
						
					case 11: // Impassable line.
					case 12: // Monsters can't cross.
					case 13: // Two sided.
					case 14: // Above unpegged.
					case 15: // Below unpegged.
					case 16: // Secret wall.
					case 17: // Soundproof wall.
					case 18: // Unmappable.
					case 19: // Mapped at start of game.
					{
						int		Toggle = 1 << (wParam - 11);
						
						// We can toggle the state of the bitset by use of
						// some maths.
						if (LineDefPointer->Data [CurrentLineDef].Bitset & Toggle)
							LineDefPointer->Data [CurrentLineDef].Bitset -= Toggle;
						else
							LineDefPointer->Data [CurrentLineDef].Bitset += Toggle;
					}
					break;

					case 20: // Trigger.
						if (HIWORD (lParam) == EN_CHANGE)
							LineDefPointer->Data [CurrentLineDef].Trigger = GetDlgItemInt (hWndDlg,wParam,&Trans,TRUE);
						break;

					case 21: // Edit the sidedefs.
						ShowWindow (hWndSideDef,SW_SHOW);
						break;
	           	}
           	}
		}
		break;

    	default:
			return FALSE;
	}
	return TRUE;
}


LONG FAR PASCAL LineDefEdit (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
		case WM_LBUTTONDOWN:
		{
			int		Result;
			HDC		DC;
			
			if (LineDefAddMode)
			{
				if (LineDefPointer)
				{
					// Add new linedefs.
					FromVertex = ToVertex;
					ToVertex = FindThatVertex (LOWORD (lParam),HIWORD (lParam));
					if (FromVertex >= 0 && ToVertex >= 0)
					{
						// We got one! Add it on.
						CurrentLineDef = LineDefPointer->Add (FromVertex,ToVertex);
						if (CurrentLineDef >= 0)
						{
							DC = GetDC (hWndMain);
							SelectObject (DC,RedPen);
							DrawLineDef (DC,CurrentLineDef);
							SelectObject (DC,GetStockObject (BLACK_PEN));
							ReleaseDC (hWndMain,DC);
						}
					}
				}
			}
			else
			{
				// Change the selected linedef.
				Result = FindThatLineDef (LOWORD (lParam),HIWORD (lParam));
				if (Result != -1)
				{
					HDC		DC;
				
					//  Set up for some drawing.
					DC = GetDC (hWndMain);
				
					// Erase old highlighted linedef.
					if (CurrentLineDef >= 0)
					{
						SelectObject (DC,YellowPen);
						DrawLineDef (DC,CurrentLineDef);
					}
				
					// Draw the new linedef.
					CurrentLineDef = Result;
					SelectObject (DC,RedPen);
					DrawLineDef (DC,CurrentLineDef);
						
					// Clean up after drawing.
					SelectObject (DC,GetStockObject (BLACK_PEN));
					ReleaseDC (hWndMain,DC);
					SendMessage (hWndLineDef,REDRAW_MESSAGE,0,0L);
				}
			}
		}
		break;

		case WM_LBUTTONDBLCLK:
			ShowWindow (hWndLineDef,SW_SHOW);
			break;
			
		case ADD_MESSAGE:
		{
			HDC		DC;
			
			// Set up for the line drawing.
			LineDefAddMode = TRUE;
			LastLineDef = CurrentLineDef;
			FromVertex = -1;
			ToVertex = -1;
				
			// Erase old highlighted linedef if it exists.
			if (CurrentLineDef >= 0)
			{
				DC = GetDC (hWndMain);
				SelectObject (DC,YellowPen);
				DrawLineDef (DC,CurrentLineDef);
				SelectObject (DC,GetStockObject (BLACK_PEN));
				ReleaseDC (hWndMain,DC);
			}
		}
		break;
    	
		case WM_RBUTTONDOWN: // Add mode off.
		{
			HDC		DC;
			int		Loop;
			
			//  Set up for some drawing.
			DC = GetDC (hWndMain);
				
			// Remove red pen highlights except for the last item.
			SelectObject (DC,YellowPen);
			if (LastLineDef >= 0)
			{
				for (Loop = LastLineDef;Loop < LineDefPointer->NoOfObjects - 1;Loop++)
					DrawLineDef (DC,Loop);
			}
			
			// Clean up after drawing.
			SelectObject (DC,GetStockObject (BLACK_PEN));
			ReleaseDC (hWndMain,DC);
			LineDefAddMode = FALSE;
			LastLineDef = -1;
		}
		break;
		
		case DELETE_MESSAGE:
			if (CurrentLineDef >= 0)
			{
				HDC		DC;
				
				// Setup time.
				DC = GetDC (hWndMain);
				
                // Erase and delete the current linedef.
				SelectObject (DC,BluePen);
				DrawLineDef (DC,CurrentLineDef);
				LineDefPointer->Delete (CurrentLineDef);
				
				// We can switch to another line now.
				CurrentLineDef--;
				if (CurrentLineDef < 0)
					CurrentLineDef = LineDefPointer->NoOfObjects - 1;
				
                // Highlight the linedef we switched to.
				if (CurrentLineDef >= 0)
				{
					SelectObject (DC,RedPen);
					DrawLineDef (DC,CurrentLineDef);
				}

				// Clean up.
				SelectObject (DC,GetStockObject (BLACK_PEN));
				ReleaseDC (hWndMain,DC);
			}
			break;
    	
		default:
			return DefWindowProc (hWnd,Message,wParam,lParam);
	}
	
	return 0L;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                           Sector Editing                               º
// º                                                                        º
// º The edit procedure makes heavy use of the "ObjectList" to store the    º
// º lines for the current sector. Be sure this list always contains the    º
// º correct information.                                                   º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

BOOL FAR PASCAL SectorProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_INITDIALOG:
    	{
			fstream		InActivityFile;
			char		Buffer [BUFFER_SIZE];
			char		Text [BUFFER_SIZE];
			int			ItemType;
			char *		CopyFrom;
			int			Loop;
			int			Add = FALSE;
		    		
			// Load info into the special dialog box.
			InActivityFile.open ("GDREDIT.INI",ios::in|ios::nocreate);
			if (!InActivityFile.fail ())
			{
				// Get to the start of the section we want.
				do
				{
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				} while (!InActivityFile.eof () && strcmp (Buffer,"[SPECIAL]") != 0);
				InActivityFile.getline (Buffer,BUFFER_SIZE);
				
				while (!InActivityFile.eof () && Buffer [0] != '[')
				{
					if (strlen (Buffer) > 0)
					{
						ItemType = atoi (Buffer);
						CopyFrom = strstr (Buffer,",");
						if (CopyFrom != NULL)
							strcpy (Text,CopyFrom + 1);
						SendDlgItemMessage (hWndDlg,16,CB_SETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,16,CB_ADDSTRING,0,(LONG) Text),(LONG) ItemType);
					}
				
					InActivityFile.getline (Buffer,BUFFER_SIZE);
				}
				InActivityFile.close ();
			}
			
			// Load the floor and ceiling textures.
			Text [8] = '\000';
			for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
			{
				if (strncmp (DoomDirectory [Loop].ResourceName,"F_END",8) == 0)
					Add = FALSE;
					
				if (Add && strncmp (DoomDirectory [Loop].ResourceName,"F1",2) != 0 && strncmp (DoomDirectory [Loop].ResourceName,"F2",2) != 0)
				{
					strncpy (Text,DoomDirectory [Loop].ResourceName,8);
					SendDlgItemMessage (hWndDlg,14,CB_ADDSTRING,0,(LONG) Text);
					SendDlgItemMessage (hWndDlg,15,CB_ADDSTRING,0,(LONG) Text);
				}
				
				if (strncmp (DoomDirectory [Loop].ResourceName,"F_START",8) == 0)
					Add = TRUE;
			}
			
			SendDlgItemMessage (hWndDlg,10,CB_SETCURSEL,0,0);
		}
   		break;
    		
    	case REDRAW_MESSAGE: // Update the window.
    	{
    		int		Loop;
    		int		Finished = FALSE;
    		int		Entry = -1;
    		char	Text [20];
    		int		MaxTypes;
    		
    		// Update our display.
    		if (SectorPointer && CurrentSector >= 0)
    		{
				// Set up the special item.
				MaxTypes = (int) SendDlgItemMessage (hWndDlg,16,CB_GETCOUNT,0,0);
				Loop = -1;
				do
				{
					Loop++;
					if (SendDlgItemMessage (hWndDlg,16,CB_GETITEMDATA,Loop,0) == SectorPointer->Data [CurrentSector].Special)
					{
						Finished = TRUE;
						Entry = Loop;
					}
				} while (!Finished && Loop < MaxTypes);
				SendDlgItemMessage (hWndDlg,16,CB_SETCURSEL,Entry,0);
					
				// Set up the edit fields.
				SetDlgItemInt (hWndDlg,10,SectorPointer->Data [CurrentSector].FloorHeight,TRUE);
				SetDlgItemInt (hWndDlg,11,SectorPointer->Data [CurrentSector].CeilingHeight,TRUE);
				SetDlgItemInt (hWndDlg,12,SectorPointer->Data [CurrentSector].Brightness,TRUE);
				SetDlgItemInt (hWndDlg,13,SectorPointer->Data [CurrentSector].Trigger,TRUE);
					
				// Now set the list boxes.
				Text [8] = '\000';
				strncpy (Text,SectorPointer->Data [CurrentSector].FloorTexture,8);
				Entry = (int) SendDlgItemMessage (hWndDlg,14,CB_FINDSTRING,0,(LONG) Text);
				SendDlgItemMessage (hWndDlg,14,CB_SETCURSEL,Entry,0);
				strncpy (Text,SectorPointer->Data [CurrentSector].CeilingTexture,8);
				Entry = (int) SendDlgItemMessage (hWndDlg,15,CB_FINDSTRING,0,(LONG) Text);
				SendDlgItemMessage (hWndDlg,15,CB_SETCURSEL,Entry,0);
    		}
    	}
    	break;
    	
    	case WM_COMMAND:
    	{
    		int		Trans;
    		char	Text [20];
    		
    		if (SectorPointer && CurrentSector >= 0)
    		{
	        	switch (wParam)
	           	{
					case 10: // Floor Height.
						if (HIWORD (lParam) == EN_CHANGE)
							SectorPointer->Data [CurrentSector].FloorHeight = GetDlgItemInt (hWndDlg,10,&Trans,TRUE);
						break;

					case 11: // Ceiling Height.
						if (HIWORD (lParam) == EN_CHANGE)
							SectorPointer->Data [CurrentSector].CeilingHeight = GetDlgItemInt (hWndDlg,11,&Trans,TRUE);
						break;

					case 12: // Sector brightness changed.
						if (HIWORD (lParam) == EN_CHANGE)
							SectorPointer->Data [CurrentSector].Brightness = GetDlgItemInt (hWndDlg,12,&Trans,TRUE);
						break;

					case 13: // Trigger has changed.
						if (HIWORD (lParam) == EN_CHANGE)
							SectorPointer->Data [CurrentSector].Trigger = GetDlgItemInt (hWndDlg,13,&Trans,TRUE);
						break;

					case 14: // Floor texture has changed.
						if (HIWORD (lParam) == CBN_SELCHANGE)
						{
							SendDlgItemMessage (hWndDlg,14,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,14,CB_GETCURSEL,0,0),(LONG) Text);
							strncpy (SectorPointer->Data [CurrentSector].FloorTexture,Text,8);
						}
						break;

					case 15: // Ceiling texture has changed.
						if (HIWORD (lParam) == CBN_SELCHANGE)
						{
							SendDlgItemMessage (hWndDlg,15,CB_GETLBTEXT,(WORD) SendDlgItemMessage (hWndDlg,15,CB_GETCURSEL,0,0),(LONG) Text);
							strncpy (SectorPointer->Data [CurrentSector].CeilingTexture,Text,8);
						}
						break;

					case 16: // Special info changed.
						if (HIWORD (lParam) == CBN_SELCHANGE)
							SectorPointer->Data [CurrentSector].Special = (int) SendDlgItemMessage (hWndDlg,16,CB_GETITEMDATA,(WORD) SendDlgItemMessage (hWndDlg,16,CB_GETCURSEL,0,0),0);
						break;
	           	}
           	}
		}
		break;
    	default:
			return FALSE;
	}
	return TRUE;
}


LONG FAR PASCAL SectorEdit (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
		case WM_LBUTTONDOWN:
		{
			int		Result;
			int		Loop;
			
			if (SectorAddMode)
			{
				if (LineDefPointer)
				{
					// Add new linedefs.
					FromVertex = ToVertex;
					ToVertex = FindThatVertex (LOWORD (lParam),HIWORD (lParam));
					if (FromVertex >= 0 && ToVertex >= 0)
					{
						HDC		DC;
				
						//  Set up for some drawing.
						DC = GetDC (hWndMain);
						SelectObject (DC,RedPen);
						
						// Check for a linedef.
						for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
						{
							// Place the sector on the right sidedef.
							if ((LineDefPointer->Data [Loop].FromVertex == FromVertex) && (LineDefPointer->Data [Loop].ToVertex == ToVertex))
							{
								// Select this line and display.
								CurrentLineDef = Loop;
								DrawLineDef (DC,CurrentLineDef);

								// Create a new sidedef if needed.
								if (LineDefPointer->Data [Loop].Sidedef1 < 0)
									LineDefPointer->Data [Loop].Sidedef1 = SideDefPointer->Add ();
								SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector = CurrentSector;
							}
							
							// Place the sector on the left sidedef.
							if ((LineDefPointer->Data [Loop].ToVertex == FromVertex) && (LineDefPointer->Data [Loop].FromVertex == ToVertex))
							{
								// Select this line and display.
								CurrentLineDef = Loop;
								DrawLineDef (DC,CurrentLineDef);
								
								// Create a new sidedef if needed.
								if (LineDefPointer->Data [Loop].Sidedef2 < 0)
									LineDefPointer->Data [Loop].Sidedef2 = SideDefPointer->Add ();
								SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector = CurrentSector;
							}
						}

						// Clean up after drawing.
						SelectObject (DC,GetStockObject (BLACK_PEN));
						ReleaseDC (hWndMain,DC);
					}
				}
			}
			else
			{
				Result = FindThatLineDef (LOWORD (lParam),HIWORD (lParam));
				if (Result != -1)
				{
					HDC		DC;
				
					//  Set up for some drawing.
					DC = GetDC (hWndMain);
				
					// Get ready to update the dialog.
					CurrentLineDef = Result;
					if (CurrentLineDef == LastLineDef)
						if (UseSide1 && LineDefPointer->Data [CurrentLineDef].Sidedef2 >= 0)
							UseSide1 = FALSE;
						else
							UseSide1 = TRUE;
					else
						UseSide1 = TRUE;

					// Select the sector from the linedef.
					if (UseSide1)
						CurrentSector = SideDefPointer->Data [LineDefPointer->Data [CurrentLineDef].Sidedef1].Sector;
					else
						CurrentSector = SideDefPointer->Data [LineDefPointer->Data [CurrentLineDef].Sidedef2].Sector;
						
					SendMessage (hWndSector,REDRAW_MESSAGE,0,0L);

					// Erase the sector.
					if (SectorOn)
					{
						SelectObject (DC,YellowPen);
						for (Loop = 0;Loop < NoOfListObjects;Loop++)
							DrawLineDef (DC,ObjectList [Loop]);
					}
				
					// Possible for no sector to be defined for a linedef.
					if (CurrentSector >= 0)
					{
						SectorOn = TRUE;
						NoOfListObjects = 0;
						SelectObject (DC,RedPen);
						for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
						{
							// Right hand sides. These MUST exist for each linedef.
							if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector == CurrentSector)
							{
								ObjectList [NoOfListObjects] = Loop;
								NoOfListObjects++;
							}

							// Left hand sides are optional.
							if (LineDefPointer->Data [Loop].Sidedef2 >= 0)
							{
								if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector == CurrentSector)
								{
									ObjectList [NoOfListObjects] = Loop;
									NoOfListObjects++;
								}
							}
						}

						// Draw the sector.
						for (Loop = 0;Loop < NoOfListObjects;Loop++)
							DrawLineDef (DC,ObjectList [Loop]);
					}
	
					// Clean up after drawing.
					SelectObject (DC,GetStockObject (BLACK_PEN));
					ReleaseDC (hWndMain,DC);
					LastLineDef = CurrentLineDef;
				}
			}
		}
		break;

		case ADD_MESSAGE:
		{
			HDC		DC;
			int		Loop;
			
			//  Set up for some drawing.
			DC = GetDC (hWndMain);

			// Erase the current sector.
			if (CurrentSector >= 0)
			{
				SelectObject (DC,YellowPen);
				for (Loop = 0;Loop < NoOfListObjects;Loop++)
					DrawLineDef (DC,ObjectList [Loop]);
			}
	
			// Add the sector and prepare to draw the lines.
			CurrentSector = SectorPointer->Add ();
			SectorAddMode = TRUE;
			FromVertex = -1;
			ToVertex = -1;
			
			// Clean up after drawing.
			SelectObject (DC,GetStockObject (BLACK_PEN));
			ReleaseDC (hWndMain,DC);
		}
		break;
    	
		case WM_RBUTTONDOWN: // Add mode off.
		{
			int		Loop;
			
			// Turn off the useful globals.
			SectorAddMode = FALSE;
			
			// We want to update the list for the new sector to be erased.
			if (CurrentSector >= 0)
			{
				SectorOn = TRUE;
				NoOfListObjects = 0;
				for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
				{
					// Right hand sides. These MUST exist for each linedef.
					if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector == CurrentSector)
					{
						ObjectList [NoOfListObjects] = Loop;
						NoOfListObjects++;
					}

					// Left hand sides are optional.
					if (LineDefPointer->Data [Loop].Sidedef2 >= 0)
					{
						if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector == CurrentSector)
						{
							ObjectList [NoOfListObjects] = Loop;
							NoOfListObjects++;
						}
					}
				}
			}
		}
		break;
		
		case DELETE_MESSAGE:
			if (CurrentSector >= 0)
			{
				HDC		DC;
				int		Loop;
				
				// Setup time.
				DC = GetDC (hWndMain);
				
				// Erase the sector.
				SelectObject (DC,YellowPen);
				for (Loop = 0;Loop < NoOfListObjects;Loop++)
					DrawLineDef (DC,ObjectList [Loop]);
				SectorPointer->Delete (CurrentSector);
				NoOfListObjects = 0;

				// Switch to the next sector.
				CurrentSector--;
				if (CurrentSector < 0)
					CurrentSector = SectorPointer->NoOfObjects - 1;
				CurrentLineDef = -1;
				LastLineDef = -1;
					
				// Draw the next sector if it exists.
				if (CurrentSector >= 0)
				{
					// Work out who appears in the new sector.
					for (Loop = 0;Loop < LineDefPointer->NoOfObjects;Loop++)
					{
						// Right hand sides. These MUST exist for each linedef.
						if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef1].Sector == CurrentSector)
						{
							ObjectList [NoOfListObjects] = Loop;
							NoOfListObjects++;
						}

						// Left hand sides are optional.
						if (LineDefPointer->Data [Loop].Sidedef2 >= 0)
						{
							if (SideDefPointer->Data [LineDefPointer->Data [Loop].Sidedef2].Sector == CurrentSector)
							{
								ObjectList [NoOfListObjects] = Loop;
								NoOfListObjects++;
							}
						}
					}
				
					// Draw the lines.
					SelectObject (DC,RedPen);
					for (Loop = 0;Loop < NoOfListObjects;Loop++)
						DrawLineDef (DC,ObjectList [Loop]);
				}
				
				// Clean up.
				SelectObject (DC,GetStockObject (BLACK_PEN));
				ReleaseDC (hWndMain,DC);
				SendMessage (hWndSector,REDRAW_MESSAGE,0,0L);
			}
			break;
		default:
			return DefWindowProc (hWnd,Message,wParam,lParam);
	}
	
	return 0L;
}

// ÉÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ»
// º                        Main Program Code                               º
// º                                                                        º
// º This controls the main application. Here we take care of the main      º
// º menu, changing the check buttons and switching between various edit    º
// º modes. The rest is boilerplate windows app.                            º
// ÈÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍÍ¼

LONG FAR PASCAL WndProc (HWND hWnd, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
		case WM_CREATE:
			if (GridSnap)
			{
				CheckMenuItem (GetMenu (hWnd),204,MF_CHECKED);
				GridSnap = TRUE;
			}
			break;
		
		case WM_COMMAND:
			switch (wParam)
			{
				case 100: // File new.
					NewWadFile ();
					break;
					
				case 101: // File open dialog.
					LoadWadFile ();
					break;
					
				case 102: // File save dialog.
					if (Registered)
					{
						if (strlen (FileName) == 0)
							SaveWadFile ();
						else
							WriteWadFile (FileName);
					}
					break;
					
				case 103: // File save as dialog.
					if (Registered)
						SaveWadFile ();
					break;
					
				case 104: // Exit program.
					DestroyWindow (hWnd);
					PostQuitMessage (0);
					break;
					
				case 200: // Thing Editor.
					if (EditMode != THING_EDIT)
					{
						CheckMenuItem (GetMenu (hWnd),wParam,MF_CHECKED);
						CheckMenuItem (GetMenu (hWnd),201,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),202,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),203,MF_UNCHECKED);
						ShowWindow (hWndSector,SW_HIDE);
						ShowWindow (hWndLineDef,SW_HIDE);
						ShowWindow (hWndSideDef,SW_HIDE);
						ShowWindow (hWndThing,SW_SHOW);
						InvalidateRect (hWndMain,NULL,TRUE);
						EditMode = THING_EDIT;
					}
					break;
					
				case 201: // Vertex Editor.
					if (EditMode != VERTEX_EDIT)
					{
						CheckMenuItem (GetMenu (hWnd),wParam,MF_CHECKED);
						CheckMenuItem (GetMenu (hWnd),200,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),202,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),203,MF_UNCHECKED);
						ShowWindow (hWndThing,SW_HIDE);
						ShowWindow (hWndSector,SW_HIDE);
						ShowWindow (hWndLineDef,SW_HIDE);
						ShowWindow (hWndSideDef,SW_HIDE);
						InvalidateRect (hWndMain,NULL,TRUE);
						EditMode = VERTEX_EDIT;
					}
					break;
					
				case 202: // Linedef Editor.
					if (EditMode != LINEDEF_EDIT)
					{
						CheckMenuItem (GetMenu (hWnd),wParam,MF_CHECKED);
						CheckMenuItem (GetMenu (hWnd),200,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),201,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),203,MF_UNCHECKED);
						ShowWindow (hWndThing,SW_HIDE);
						ShowWindow (hWndSector,SW_HIDE);
						ShowWindow (hWndLineDef,SW_HIDE);
						ShowWindow (hWndSideDef,SW_HIDE);
						InvalidateRect (hWndMain,NULL,TRUE);
						EditMode = LINEDEF_EDIT;
					}
					break;
					
				case 203: // Sector Editor.
					if (EditMode != SECTOR_EDIT)
					{
						CheckMenuItem (GetMenu (hWnd),wParam,MF_CHECKED);
						CheckMenuItem (GetMenu (hWnd),200,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),201,MF_UNCHECKED);
						CheckMenuItem (GetMenu (hWnd),202,MF_UNCHECKED);
						ShowWindow (hWndThing,SW_HIDE);
						ShowWindow (hWndLineDef,SW_HIDE);
						ShowWindow (hWndSideDef,SW_HIDE);
						ShowWindow (hWndSector,SW_SHOW);
						InvalidateRect (hWndMain,NULL,TRUE);
						EditMode = SECTOR_EDIT;
					}
					break;
					
					case 204: // Gridsnap.
						if (!GridSnap)
						{
							CheckMenuItem (GetMenu (hWnd),wParam,MF_CHECKED);
							GridSnap = TRUE;
						}
						else
						{
							CheckMenuItem (GetMenu (hWnd),wParam,MF_UNCHECKED);
							GridSnap = FALSE;
						}
						break;
						
            	default:
                	return DefWindowProc (hWnd,Message,wParam,lParam);
			}
		break;

	   	case WM_CLOSE:
			DestroyWindow (hWnd);
			PostQuitMessage (0);
	       	break;

		case WM_PAINT:
		{
			PAINTSTRUCT		PS;
			HDC				DC;
			
			DC = BeginPaint (hWnd,&PS);
			PaintIt (DC);
			EndPaint (hWnd,&PS);
		}
		break;
		
		default:
			switch (EditMode)
			{
				case THING_EDIT:
					return ThingEdit (hWnd,Message,wParam,lParam);
					break;
				
				case VERTEX_EDIT:
					return VertexEdit (hWnd,Message,wParam,lParam);
					break;
				
				case SECTOR_EDIT:
					return SectorEdit (hWnd,Message,wParam,lParam);
					break;
				
				case LINEDEF_EDIT:
					return LineDefEdit (hWnd,Message,wParam,lParam);
					break;
				
				default:
                	return DefWindowProc (hWnd,Message,wParam,lParam);
			}
	}
	
	return 0L;
}


BOOL FAR PASCAL CheapProc (HWND hWndDlg, WORD Message, WORD wParam, LONG lParam)
{
	switch (Message)
	{
    	case WM_CLOSE:
         	PostMessage (hWndDlg,WM_COMMAND,IDCANCEL,0L);
         	break;

    	case WM_COMMAND:
        	switch (wParam)
           	{
            	case IDOK:
                 	EndDialog (hWndDlg,TRUE);
                 	break;
                 	
            	case IDCANCEL:
                 	EndDialog (hWndDlg,FALSE);
                 	break;
           	}
         	break;

    	default:
    		return FALSE;
	}
	return TRUE;
}


int PASCAL WinMain (HANDLE hInstance, HANDLE hPrevInstance, LPSTR lpszCmdLine, int nCmdShow)
{
	MSG       	msg;
	WNDCLASS	wndclass;
	int			FileHandle;
	FARPROC		lpfnButtonBarMsgProc;
	FARPROC		lpfnThingProc;
	FARPROC		lpfnSectorProc;
	FARPROC		lpfnLineDefProc;
	FARPROC		lpfnSideDefProc;
	char		Text [20];
	int			Loop;

	hInst = hInstance;
	
	// Create some pens. More efficient this way.
	YellowPen = CreatePen (PS_SOLID,1,YELLOW_COLOUR);
	RedPen = CreatePen (PS_SOLID,1,RED_COLOUR);
	BluePen = CreatePen (PS_SOLID,1,BLUE_COLOUR);
	RedBrush = CreateSolidBrush (RED_COLOUR);
	BlueBrush = CreateSolidBrush (BLUE_COLOUR);
	
	// Read our configuration information.
	GetPrivateProfileString (SECTION_NAME,"DoomDirectory","C:\\DOOM",DoomWadPath,sizeof (DoomWadPath),PROFILE_FILE);
	sprintf (DoomWadName,"%s\\DOOM.WAD",DoomWadPath);
	GridSnap = GetPrivateProfileInt (SECTION_NAME,"GridSnap",32,PROFILE_FILE);
	GridSize = GetPrivateProfileInt (SECTION_NAME,"GridSize",32,PROFILE_FILE);
	
	// Register the window class.
	if (!hPrevInstance)
	{
		// Register window classes if first instance of application.
		memset (&wndclass,0x00,sizeof (WNDCLASS));
		wndclass.style = CS_HREDRAW | CS_VREDRAW | CS_BYTEALIGNWINDOW | CS_DBLCLKS;
		wndclass.lpfnWndProc = WndProc;
		wndclass.cbClsExtra = 0;
		wndclass.cbWndExtra = 0;
		wndclass.hInstance = hInst;
		wndclass.hIcon = LoadIcon (hInst,"MAIN_ICON");
		wndclass.hCursor = LoadCursor (NULL,IDC_ARROW);
		wndclass.hbrBackground = BlueBrush;
		wndclass.lpszMenuName = "MAIN_MENU";
		wndclass.lpszClassName = MainClassName;
		RegisterClass (&wndclass);
	}

	// Main window.
	hWndMain = CreateWindow (
		MainClassName,              /* Window class name           */
		"GDR Doom Thing Editor",/* Window's title              */
		WS_CAPTION      |       /* Title and Min/Max           */
		WS_SYSMENU      |       /* Add system menu box         */
		WS_MINIMIZEBOX  |       /* Add minimize box            */
		WS_MAXIMIZEBOX  |       /* Add maximize box            */
		WS_THICKFRAME   |       /* thick sizeable frame        */
		WS_CLIPCHILDREN |       /* don't draw in child windows areas */
		WS_OVERLAPPED,
		CW_USEDEFAULT, 0,       /* Use default X, Y            */
		CW_USEDEFAULT, 0,       /* Use default X, Y            */
		NULL,                   /* Parent window's handle      */
		NULL,                   /* Default to Class Menu       */
		hInst,                  /* Instance of window          */
		NULL);                  /* Create struct for WM_CREATE */

	if (hWndMain == NULL)
	{
		MessageBox (NULL,"Unable to create the main window.",NULL,MB_ICONEXCLAMATION);
		return -1;
	}

	// Read in the DOOM.WAD stuff for wall patches and the like.
	FileHandle = open (DoomWadName,O_BINARY|O_RDONLY);
	read (FileHandle,&DoomHeader,sizeof (Header));
	DoomDirectory = new DirectoryEntry [DoomHeader.NumberOfEntries];
	lseek (FileHandle,DoomHeader.DirectoryPointer,SEEK_SET);
	lread (FileHandle,DoomDirectory,sizeof (DirectoryEntry) * DoomHeader.NumberOfEntries);
	close (FileHandle);
	
	// Check for registered version.
	for (Loop = 0;Loop < DoomHeader.NumberOfEntries;Loop++)
		if (strncmp (DoomDirectory [Loop].ResourceName,"E2M2",8) == 0)
			Registered = TRUE;

	// Cheapskate message.
	if (!Registered)
	{
		FARPROC		MsgProc;
		
		MsgProc = MakeProcInstance ((FARPROC) CheapProc,hInst);
		DialogBox (hInst,(LPSTR) "CHEAP_BUGGERS",hWndMain,MsgProc);
		FreeProcInstance (MsgProc);
	}
	
	// Create the button bar dialog box.
	lpfnButtonBarMsgProc = MakeProcInstance ((FARPROC) ButtonBarMsgProc,hInst);
	hWndButtonBar = CreateDialog (hInst,(LPSTR) "BUTTON_BAR",hWndMain,lpfnButtonBarMsgProc);

	// Create the thing dialog box.
	lpfnThingProc = MakeProcInstance ((FARPROC) ThingProc,hInst);
	hWndThing = CreateDialog (hInst,(LPSTR) "THING_EDITOR",hWndMain,lpfnThingProc);

	// Create the sector dialog box.
	lpfnSectorProc = MakeProcInstance ((FARPROC) SectorProc,hInst);
	hWndSector = CreateDialog (hInst,(LPSTR) "SECTOR_EDITOR",hWndMain,lpfnSectorProc);

	// Create the linedef dialog box.
	lpfnLineDefProc = MakeProcInstance ((FARPROC) LineDefProc,hInst);
	hWndLineDef = CreateDialog (hInst,(LPSTR) "LINEDEF_EDITOR",hWndMain,lpfnLineDefProc);

	// Create the sidedef dialog box.
	lpfnSideDefProc = MakeProcInstance ((FARPROC) SideDefProc,hInst);
	hWndSideDef = CreateDialog (hInst,(LPSTR) "SIDEDEF_EDITOR",hWndMain,lpfnSideDefProc);

	ShowWindow (hWndMain,SW_SHOWMAXIMIZED);

	while (GetMessage (&msg,NULL,0,0))
	{
		if (!IsDialogMessage (hWndThing,&msg) && !IsDialogMessage (hWndButtonBar,&msg) && !IsDialogMessage (hWndSector,&msg))
		{
			TranslateMessage (&msg);
			DispatchMessage (&msg);
		}
	}

	// Remove our button bar from existence.
	DestroyWindow (hWndButtonBar);
	FreeProcInstance (lpfnButtonBarMsgProc);

	// Remove our thing dialog from existence.
	DestroyWindow (hWndThing);
	FreeProcInstance (lpfnThingProc);

	// Remove our sector dialog from existence.
	DestroyWindow (hWndSector);
	FreeProcInstance (lpfnSectorProc);

	// Remove our linedef dialog from existence.
	DestroyWindow (hWndLineDef);
	FreeProcInstance (lpfnLineDefProc);

	// Remove our linedef dialog from existence.
	DestroyWindow (hWndSideDef);
	FreeProcInstance (lpfnSideDefProc);

	// Delete our pens.
	DeleteObject (YellowPen);
	DeleteObject (RedPen);
	DeleteObject (BluePen);
	DeleteObject (BlueBrush);
	DeleteObject (RedBrush);
	
	// Write our configuration information.
	sprintf (Text,"%d",GridSnap);
	WritePrivateProfileString (SECTION_NAME,"GridSnap",Text,PROFILE_FILE);
	sprintf (Text,"%d",GridSize);
	WritePrivateProfileString (SECTION_NAME,"GridSize",Text,PROFILE_FILE);
	
	// Clean up before exiting from the application.
	UnregisterClass (MainClassName,hInst);
	UnloadWadFile ();
		
	return msg.wParam;
}
